Agentic Development Part 3: Mendesain Task untuk Agent
Part 2 membedah planner, tool use, memory, dan observation sebagai komponen yang membangun loop agent. Tapi seberapa baik pun keempat komponen itu bekerja, hasilnya tetap dibatasi oleh kualitas input yang diberikan ke planner di awal — yaitu task itu sendiri. Plan dinamis dari planner yang baik tetap akan tersesat kalau task yang diberikan ambigu; observation yang teliti tetap tidak bisa memverifikasi apa-apa kalau tidak ada definisi “selesai” yang jelas untuk dirujuk. Part 4 series Spec-Driven Development sudah membahas bagaimana spec diterjemahkan jadi task group secara umum — artikel ini menurunkan pembahasan itu satu level lebih dalam, ke disiplin mendesain task individual: granularitas yang tepat, definisi selesai yang tidak ambigu, dependency antar task, dan bagaimana menangani task yang sifatnya eksploratif dan tidak bisa didefinisikan presisi sejak awal.
Kenapa Desain Task Bukan Sekadar Memecah Pekerjaan
Ada anggapan keliru yang umum: bahwa mendesain task untuk agent hanyalah soal “membuat potongan pekerjaan lebih kecil”. Memecah pekerjaan jadi kecil memang perlu, tapi itu kondisi yang perlu, bukan kondisi yang cukup. Task yang kecil tapi salah potong tetap menghasilkan masalah — hanya dalam skala yang lebih kecil dan kadang lebih sulit dilacak karena tersembunyi di antara banyak task lain yang terlihat baik-baik saja.
Pertimbangkan dua cara memecah pekerjaan “tambahkan validasi pada form registrasi”:
PEMECAHAN BURUK (kecil tapi salah potong):
Task A: Tambahkan validasi email
Task B: Tambahkan validasi password
Task C: Tambahkan validasi nomor telepon
Task D: Perbaiki pesan error supaya konsisten
Masalah: Task D bergantung pada bagaimana Task A, B, C masing-masing
menulis pesan error-nya. Kalau ketiganya dikerjakan secara independen
oleh agent yang berbeda tanpa konvensi bersama, Task D akan menemukan
tiga gaya pesan error yang berbeda dan harus menormalisasi — pekerjaan
yang seharusnya tidak perlu terjadi kalau scope dipotong dengan tepat.
PEMECAHAN BAIK (potongan selaras dengan batas yang sebenarnya):
Task A: Definisikan konvensi pesan error validasi (format, bahasa,
struktur response) sebagai referensi bersama
Task B: Implementasikan validasi email, password, dan nomor telepon
mengikuti konvensi dari Task A
Task ini lebih sedikit jumlahnya, tapi batasnya selaras dengan
ketergantungan yang sebenarnya ada di pekerjaan tersebut.
Prinsip di balik perbedaan ini: task yang baik dipotong di sepanjang batas yang secara alami independen, bukan sekadar dipotong berdasarkan ukuran. Memecah pekerjaan terlalu dini berdasarkan “supaya kelihatan kecil” tanpa memperhatikan ketergantungan yang sebenarnya ada justru menciptakan kerja koordinasi tambahan yang tidak perlu.
Pertanyaan yang lebih berguna daripada “seberapa kecil task ini” adalah “apakah task ini bisa diselesaikan dan diverifikasi tanpa perlu menunggu keputusan dari task lain yang belum selesai”. Kalau jawabannya tidak, batas task perlu digambar ulang, bukan sekadar dipecah lebih kecil lagi.
Granularitas: Terlalu Besar vs Terlalu Kecil
Setelah batas task ditentukan dengan benar, pertanyaan berikutnya adalah ukurannya. Granularitas yang keliru di kedua arah sama-sama bermasalah, hanya dengan gejala yang berbeda.
Task yang terlalu besar menghasilkan masalah yang sudah disinggung di Part 1: sulit diverifikasi karena terlalu banyak hal berubah sekaligus, sulit di-review karena reviewer harus memahami keseluruhan konteks baru sebelum bisa menilai bagian mana yang benar dan mana yang salah, dan ketika ada kesalahan di tengah jalan, biaya untuk melacak akar masalahnya jauh lebih tinggi karena harus menelusuri seluruh perubahan besar tersebut.
Task yang terlalu kecil punya gejala yang berlawanan tapi sama merepotkannya: overhead koordinasi antar task menjadi lebih besar daripada nilai pekerjaan itu sendiri. Setiap task butuh konteks untuk dipahami, checkpoint untuk direview, dan kemungkinan butuh sinkronisasi dengan task lain yang terkait. Kalau task dipecah terlalu halus, waktu yang dihabiskan mengoordinasikan task-task kecil ini bisa melebihi waktu yang dihemat dari memecahnya.
flowchart LR
A[Task Terlalu Besar] --> B[Sulit Diverifikasi]
A --> C[Review Butuh Pemahaman Konteks Penuh]
A --> D[Biaya Lacak Kesalahan Tinggi]
E[Task Terlalu Kecil] --> F[Overhead Koordinasi Tinggi]
E --> G[Sinkronisasi Antar Task Berlebihan]
E --> H[Nilai Pekerjaan per Task Tidak Sepadan dengan Overhead]
I[Granularitas Tepat] --> J[Bisa Diverifikasi Independen]
I --> K[Review Tanpa Perlu Pahami Konteks Penuh dari Nol]
I --> L[Overhead Koordinasi Wajar]Heuristik praktis yang bisa dipakai sebagai titik awal: satu task idealnya menghasilkan satu unit perubahan yang bisa direview dan dipahami dalam satu sesi singkat tanpa perlu membuka task lain untuk memahami konteksnya, dan punya satu definisi selesai yang jelas tanpa perlu dipecah lagi menjadi sub-kriteria yang banyak. Kalau menjelaskan “apa yang membuat task ini selesai” sudah membutuhkan daftar panjang dengan banyak kondisi independen, itu sinyal task perlu dipecah. Kalau sebaliknya — task begitu sempit hingga konteksnya tidak bisa dipahami tanpa membaca beberapa task lain dulu — itu sinyal batasnya terlalu halus dan perlu digabung.
Tidak ada angka pasti seperti “satu task harus X baris kode” atau “X menit pengerjaan”. Ukuran yang tepat bergantung pada kompleksitas domain, bukan ukuran fisik perubahan. Task yang mengubah satu baris konfigurasi tapi berdampak luas ke seluruh sistem bisa lebih “besar” secara substansi dibanding task yang menulis seratus baris kode boilerplate yang straightforward.
Mendefinisikan “Selesai” yang Tidak Ambigu
Acceptance criteria di level spec, seperti dibahas di Part 2 series Spec-Driven Development, biasanya mencakup keseluruhan fitur. Definition of done di level task adalah turunan yang lebih sempit — subset acceptance criteria yang relevan khusus untuk task tersebut, ditambah kriteria teknis spesifik yang mungkin tidak disebutkan eksplisit di spec level fitur tapi tetap perlu jelas di level eksekusi.
Task: Implementasikan endpoint POST /password-reset/request
Definition of Done:
□ Endpoint menerima request sesuai schema OpenAPI yang sudah
didefinisikan (rujukan: spec.md bagian Acceptance Criteria #1-2)
□ Rate limiting 3 request/email/jam berfungsi dan teruji
□ Token disimpan dalam bentuk hash, bukan plaintext (constraint
keamanan dari spec.md)
□ Test unit untuk validasi email dan test integration untuk
endpoint lengkap, keduanya lolos
□ Tidak mengubah skema tabel users yang sudah ada (non-goals
dari spec.md)
Definisi selesai yang baik selalu bisa dirujuk balik ke dokumen yang lebih tinggi — spec, constraint, atau non-goals — bukan diciptakan baru secara terpisah tanpa hubungan jelas ke kontrak yang sudah disepakati. Ini mencegah definisi selesai di level task menyimpang diam-diam dari yang sebenarnya dimaksud di level spec.
Yang membedakan definition of done yang baik dari yang buruk bukan panjangnya, tapi keterujiannya. Setiap baris idealnya bisa dijawab pasti dengan ya atau tidak, tanpa perlu interpretasi tambahan — sama seperti prinsip acceptance criteria yang testable dari Part 2 series sebelumnya, hanya diterapkan di skala yang lebih kecil dan lebih spesifik teknis.
Independensi dan Dependency Antar Task
Beberapa task bisa dikerjakan secara paralel tanpa saling mengganggu; beberapa lainnya harus menunggu task tertentu selesai lebih dulu. Mengidentifikasi mana yang mana adalah bagian penting dari desain task yang sering diabaikan sampai dua agent (atau dua sesi kerja) bentrok mengerjakan hal yang saling bergantung secara bersamaan.
Dependency graph sederhana membantu memvisualisasikan ini sebelum eksekusi dimulai:
flowchart TD
A[Task 1: Skema Database] --> B[Task 2: Endpoint Request Reset]
A --> C[Task 3: Endpoint Confirm Reset]
B --> D[Task 4: Rate Limiting pada Endpoint Request]
C --> E[Task 5: Validasi Token pada Endpoint Confirm]
D --> F[Task 6: Audit Log]
E --> FDari graph ini terlihat jelas: Task 2 dan Task 3 bisa dikerjakan paralel begitu Task 1 selesai, karena keduanya tidak saling bergantung satu sama lain — hanya sama-sama bergantung pada skema database. Tapi Task 6 (audit log) harus menunggu Task 4 dan Task 5 selesai, karena audit log mencatat hasil dari kedua endpoint tersebut.
Tanpa dependency graph yang eksplisit, risiko yang muncul adalah dua task yang sebenarnya saling bergantung dieksekusi seolah-olah independen — menghasilkan konflik yang baru terdeteksi saat mencoba menggabungkan hasilnya, bukan dicegah sejak awal. Sebaliknya, task yang sebenarnya independen tapi diperlakukan sebagai sekuensial hanya membuang waktu menunggu yang tidak perlu.
Sebelum eksekusi dimulai, luangkan waktu menggambar dependency antar task — bahkan untuk project kecil. Investasi kecil ini mencegah situasi di mana dua sesi kerja (baik dengan agent yang sama di waktu berbeda, atau agent berbeda secara paralel) mengasumsikan hal yang berbeda tentang state yang sama.
Scope Boundary: Mencegah Task Melebar
Non-goals di level spec, seperti dibahas di Part 2 series Spec-Driven Development, mencegah keseluruhan fitur melebar dari yang dimaksud. Tapi scope creep juga terjadi di level yang lebih kecil — task individual yang awalnya didefinisikan sempit, lalu mengembang selama eksekusi karena agent menemukan hal-hal yang “terlihat terkait” dan memutuskan untuk sekalian menanganinya.
ANTI-PATTERN (task tanpa scope boundary, mengembang saat eksekusi):
Task: Perbaiki validasi password supaya mengikuti kebijakan baru
(minimal 8 karakter, kombinasi huruf dan angka)
Yang terjadi: agent menemukan bahwa pesan error validasi password
saat ini juga kurang jelas, lalu sekalian memperbaiki seluruh sistem
pesan error di form registrasi, lalu menemukan field nomor telepon
juga tidak tervalidasi dengan baik, lalu sekalian menambahkan
validasi untuk itu juga.
Task yang awalnya sempit berakhir menyentuh empat bagian berbeda
dari form registrasi, sulit direview, dan sulit dipisahkan mana
perubahan yang diminta dan mana yang inisiatif tambahan.
BENAR (scope boundary eksplisit di level task):
Task: Perbaiki validasi password supaya mengikuti kebijakan baru
(minimal 8 karakter, kombinasi huruf dan angka)
Scope Boundary:
- HANYA mengubah logika validasi password, tidak menyentuh
validasi field lain
- HANYA mengubah pesan error untuk kasus password tidak valid,
tidak menormalisasi pesan error field lain
- Jika ditemukan masalah lain yang terkait (misal pesan error
field lain juga kurang jelas), catat sebagai temuan terpisah,
JANGAN diperbaiki dalam task ini
Pola “catat sebagai temuan terpisah, jangan diperbaiki dalam task ini” penting untuk dipahami sebagai instruksi eksplisit, bukan dianggap sudah jelas dengan sendirinya. Agent yang menemukan hal yang terlihat related secara wajar akan cenderung ingin “membantu” menyelesaikannya sekalian — niat ini sebenarnya masuk akal dari sudut pandang agent, tapi tanpa batas eksplisit, niat baik ini justru menghasilkan task yang sulit di-review dan sulit dipisahkan tanggung jawabnya.
Task untuk Pekerjaan yang Ambigu atau Eksploratif
Tidak semua pekerjaan bisa didefinisikan presisi sejak awal. Kadang yang dibutuhkan justru eksplorasi untuk memahami sesuatu sebelum bisa mendefinisikan task implementasi yang konkret — misalnya investigasi penyebab bug yang belum diketahui akar masalahnya, atau riset pendekatan teknis sebelum keputusan arsitektur bisa diambil.
Memaksakan format “definition of done yang testable” pada pekerjaan semacam ini tidak masuk akal, karena pada dasarnya belum diketahui apa hasil akhirnya akan seperti apa. Solusinya bukan menghindari mendefinisikan task untuk pekerjaan eksploratif, tapi mengubah jenis outputnya: dari “kode yang berfungsi” menjadi “temuan yang terdokumentasi”.
Task (tipe: spike/eksplorasi): Investigasi penyebab response time
endpoint search yang melambat signifikan di production
Batasan Waktu/Scope Eksplorasi:
- Maksimal investigasi mencakup: query database, index yang
dipakai, dan pola traffic dalam 7 hari terakhir
- TIDAK mengubah kode atau konfigurasi apapun di tahap ini
Output yang Diharapkan (bukan kode, tapi temuan):
- Identifikasi kandidat penyebab (atau beberapa kandidat jika
belum bisa dipastikan satu)
- Data pendukung (query plan, metrik, pola yang ditemukan)
- Rekomendasi langkah berikutnya, yang akan menjadi task
implementasi terpisah setelah hasil investigasi ini direview
Pola “spike task” ini — istilah yang dipinjam dari praktik agile, dipakai di sini dalam konteks yang sama — dibatasi secara eksplisit dari dua arah: scope eksplorasi yang boleh disentuh, dan larangan eksplisit untuk mengubah kode dalam task ini. Begitu temuan dari spike task direview manusia, baru task implementasi konkret didefinisikan berdasarkan temuan tersebut — sehingga task implementasi tetap punya definition of done yang jelas dan testable, hanya saja informasinya datang dari hasil spike, bukan diasumsikan sejak awal.
flowchart LR
A[Kondisi Ambigu/Belum Dipahami] --> B[Spike Task: Eksplorasi Terbatas]
B --> C[Temuan Terdokumentasi]
C --> D[Review Manusia]
D --> E[Task Implementasi dengan Definition of Done Jelas]Risiko terbesar pada spike task adalah batas yang tidak dijaga ketat — agent yang sedang investigasi menemukan “perbaikan kecil yang jelas” di tengah eksplorasi dan langsung menerapkannya, padahal larangan mengubah kode di tahap ini ada untuk alasan yang jelas: keputusan implementasi sebaiknya menunggu temuan lengkap dan review, bukan diambil setengah jalan saat pemahaman masih parsial.
Template Penulisan Task
Menggabungkan seluruh prinsip di atas, berikut template praktis untuk menulis task individual yang bisa langsung dipakai:
## Task: [Nama Task Singkat dan Spesifik]
### Goal
[Satu atau dua kalimat: apa yang ingin dicapai task ini]
### Context
[Rujukan ke spec/dokumen yang relevan — bagian mana dari spec.md
yang menjadi dasar task ini. Informasi latar yang dibutuhkan agent
untuk memahami kenapa task ini ada, tanpa perlu membaca ulang
keseluruhan spec]
### Constraint
[Batasan teknis yang berlaku khusus untuk task ini, diturunkan dari
constraint level spec yang relevan]
### Scope Boundary
[Apa yang secara eksplisit TIDAK termasuk task ini — terutama hal
yang mungkin terlihat "related" dan tergoda untuk sekalian dikerjakan]
### Dependency
[Task apa yang harus selesai lebih dulu, dan task apa yang
menunggu task ini selesai]
### Definition of Done
□ [Kriteria 1, testable, dirujuk ke acceptance criteria spec]
□ [Kriteria 2]
□ [Kriteria 3]
Untuk task bertipe spike/eksploratif, bagian “Definition of Done” diganti dengan “Output yang Diharapkan” seperti dicontohkan di bagian sebelumnya, dan ditambah bagian “Batasan Waktu/Scope Eksplorasi” untuk menjaga eksplorasi tidak melebar tanpa batas.
Template ini tidak harus diisi panjang untuk setiap bagian — task sederhana bisa punya isi satu baris di tiap bagian. Yang penting bukan panjangnya, tapi bahwa setiap bagian dipikirkan secara eksplisit, bukan diasumsikan “sudah jelas dari konteks”.
Anti-Pattern dalam Desain Task
Beberapa pola yang sering muncul dan melemahkan efektivitas task meski terlihat sudah “dipecah-pecah dengan baik” secara permukaan:
Task yang terlalu vague. Instruksi seperti “perbaiki bug ini” atau “tingkatkan performa endpoint ini” tanpa definisi konkret apa yang dianggap “diperbaiki” atau “ditingkatkan”. Task semacam ini memaksa agent menebak kriteria sukses sendiri, dengan hasil yang bisa sangat bervariasi tergantung interpretasi.
Task yang menggabungkan beberapa concern sekaligus. Satu task yang mencakup perubahan skema database, logika bisnis, dan tampilan UI sekaligus — meskipun secara nominal “satu task”, task ini sebenarnya menggabungkan beberapa unit kerja yang seharusnya independen, menghasilkan masalah granularitas yang sudah dibahas di atas meski jumlah task yang tercatat tetap satu.
Task tanpa dependency yang jelas hingga bentrok saat paralel. Dua task yang sama-sama mengasumsikan mereka “yang pertama” mengubah suatu file atau skema, dikerjakan bersamaan tanpa graph dependency yang eksplisit, menghasilkan konflik yang baru terdeteksi saat mencoba menggabungkan kedua hasil.
Scope boundary yang hanya implisit. Mengandalkan agent untuk “tahu sendiri” batas task tanpa menyatakannya eksplisit — seperti dibahas di bagian scope boundary, niat baik agent untuk membantu lebih jauh dari yang diminta justru menghasilkan task yang sulit direview dan sulit dipisahkan tanggung jawabnya.
Memaksakan format testable pada pekerjaan yang sebenarnya eksploratif. Mencoba menulis definition of done yang presisi untuk pekerjaan yang pada dasarnya butuh investigasi dulu — hasilnya definition of done yang dipaksakan dan tidak benar-benar mencerminkan apa yang sebenarnya dibutuhkan dari task tersebut. Gunakan pola spike task untuk kasus ini, bukan memaksakan template task implementasi biasa.
Task yang ambigu di awal tidak menjadi lebih jelas seiring eksekusi berjalan — ambiguitas itu hanya berpindah bentuk menjadi keputusan implisit yang diambil agent secara sepihak, yang baru terlihat saat hasil akhirnya direview. Menyelesaikan ambiguitas sebelum eksekusi dimulai selalu lebih murah daripada menguraikannya setelah kode sudah ditulis.
Ringkasan
- Mendesain task bukan sekadar memecah pekerjaan jadi kecil — batas task yang baik selaras dengan ketergantungan yang sebenarnya ada, bukan dipotong berdasarkan ukuran semata
- Granularitas yang keliru bermasalah di kedua arah: task terlalu besar sulit diverifikasi, task terlalu kecil menciptakan overhead koordinasi yang tidak sepadan dengan nilai pekerjaannya
- Definition of done di level task adalah turunan sempit dari acceptance criteria di level spec, ditambah kriteria teknis spesifik — selalu bisa dirujuk balik ke dokumen yang lebih tinggi
- Dependency graph antar task mencegah dua task yang saling bergantung dieksekusi seolah independen, dan sebaliknya mencegah task independen diperlakukan sekuensial tanpa perlu
- Scope boundary di level task sama pentingnya dengan non-goals di level spec — tanpa batas eksplisit, task kecil bisa mengembang karena agent menemukan hal “terkait” yang ingin sekalian ditangani
- Untuk pekerjaan yang belum bisa didefinisikan presisi, gunakan pola spike task: dibatasi scope eksplorasi, dilarang mengubah kode, dan menghasilkan temuan terdokumentasi alih-alih kode — baru jadi task implementasi setelah direview
- Hindari task yang vague, task yang menggabungkan beberapa concern sekaligus, dependency yang hanya implisit, dan memaksakan format testable pada pekerjaan yang sebenarnya butuh eksplorasi dulu