[{"data":1,"prerenderedAt":299},["ShallowReactive",2],{"tag-labels":3,"site-header":4,"article-tiap-nota-isinya-pesanan-yang-sama-id":72},{},{"featuredPost":5,"navigations":29},{"id":6,"title":7,"title_id":8,"slug":9,"excerpt":10,"excerpt_id":11,"has_translation":12,"published_at":13,"reading_time":14,"hidden_from_listing":15,"tags":16,"thumbnail_image":21,"og_image":22,"series":23,"authors":28},110,"When You Pick a Framework Just to Look Different","Ketika Pilih Framework Cuma Biar Kelihatan Beda","ketika-pilih-framework-cuma-biar-kelihatan-beda","The last time you chose a new framework or library, was it because your problem needed that tool, or because it sounded cool and few people around you were using it? That question is uncomfortable. Technology decisions are often made for reasons that never appear in any technical document.","Terakhir kali kamu milih framework atau library baru, itu karena masalahmu butuh alat itu, atau karena alatnya kedengaran keren dan belum banyak yang pakai? Pertanyaan ini nggak enak dijawab. Soalnya keputusan teknologi sering diambil dengan alasan yang nggak pernah ditulis di dokumen teknis mana pun.",true,"2026-10-06T15:25:21.000Z",5,false,[17,18,19,20],"karier","framework","opini","pengalaman",null,"\u002Fog\u002Fketika-pilih-framework-cuma-biar-kelihatan-beda.png",{"id":24,"title":25,"title_id":26,"slug":27},9,"Articles","Artikel","all",[],[30,46,67],{"collection":31,"item":32},"series",{"__typename":31,"title":25,"title_id":26,"slug":27,"children_series":33},[34,38,42],{"title":35,"title_id":36,"slug":37},"Artificial Intelligence","Kecerdasan Artifisial","kecerdasan-artifisial",{"title":39,"title_id":40,"slug":41},"Programming Fundamentals","Konsep Dasar Pemrograman","konsep-dasar-pemrograman",{"title":43,"title_id":44,"slug":45},"Algorithms and Data Structures","Algoritma dan Struktur Data","algoritma-struktur-data",{"collection":31,"item":47},{"__typename":31,"title":48,"title_id":48,"slug":49,"children_series":50},"Tutorial","tutorial",[51,55,59,63],{"title":52,"title_id":53,"slug":54},"Learning Python","Belajar Python","belajar-python",{"title":56,"title_id":57,"slug":58},"Learning Go","Belajar Golang","belajar-golang",{"title":60,"title_id":61,"slug":62},"Learning Java","Belajar Java","belajar-java",{"title":64,"title_id":65,"slug":66},"Learning Pascal","Belajar Pascal","belajar-pascal",{"collection":31,"item":68},{"__typename":31,"title":69,"title_id":69,"slug":70,"children_series":71},"Lab","lab",[],{"article":73,"renderedBody":175,"renderedTakeaway":176,"latestArticles":177,"toc":276},{"id":74,"title":75,"title_id":75,"slug":76,"excerpt":77,"excerpt_id":77,"has_translation":12,"published_at":78,"reading_time":79,"hidden_from_listing":15,"tags":80,"thumbnail_image":21,"og_image":84,"series":85,"authors":167,"body_locale":170,"available_locales":171,"body":173,"takeway":174,"prerequisite":21},120,"Tiap Nota di Kafe Ini Isinya Pesanan yang Sama","tiap-nota-isinya-pesanan-yang-sama","Nota tiap meja isinya sama semua, dan totalnya Rp0 — padahal tiap meja cuma pesan satu barang. Coba tebak kenapa.","2026-10-11T23:00:15.000Z",4,[81,82,83,70],"python","scope","mutasi","\u002Fog\u002Ftiap-nota-isinya-pesanan-yang-sama.png",{"id":86,"title":69,"title_id":69,"slug":70,"description":87,"description_id":88,"articles":89,"children_series":166,"parent_series":21},29,"Practice labs: work the problem first, then read the answer. Every lab ships with a repo you can clone, production-sized data, and a target you have to hit yourself. The write-up lives in this series too, and it only opens from the problem article.","Lab praktik: kerjakan masalahnya dulu, baru lihat jawabannya. Tiap lab punya repo yang bisa di-clone, data seukuran produksi, dan target yang harus kamu capai sendiri. Artikel jawabannya ada di seri ini juga, dan cuma bisa kamu buka dari artikel masalahnya.",[90,111,128,144,151],{"id":91,"title":92,"title_id":93,"slug":94,"excerpt":95,"excerpt_id":96,"has_translation":12,"published_at":97,"reading_time":14,"hidden_from_listing":15,"tags":98,"thumbnail_image":21,"og_image":102,"series":103,"authors":104},104,"N+1 Was Never the Bottleneck: 41 Queries Down to 2, Still One Second","Query-nya Tinggal 2, Tapi Response-nya Masih 1 Detik","query-tinggal-2-tapi-response-masih-1-detik","A feed endpoint that shows 20 posts takes a full second and hits the database with 41 queries. Apply the standard N+1 advice, get it down to 2 queries, and the latency does not move. This is that problem as it happened, plus the lab.","Endpoint feed yang cuma menampilkan 20 tulisan butuh satu detik, dan server database dihujani 41 query. Kamu kerjakan nasihat N+1 yang standar, query-nya turun jadi 2, dan latency-nya nggak bergerak. Ini versi \"apa adanya\" dari masalah itu, plus lab-nya.","2026-10-04T09:59:21.000Z",[99,100,101],"database","performance","backend","\u002Fog\u002Fquery-tinggal-2-tapi-response-masih-1-detik.png",{"id":86,"title":69,"title_id":69,"slug":70},[105],{"authors_id":106},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},1,"Daffa Izzuddin","izzudd","Writer",{"id":112,"title":113,"title_id":114,"slug":115,"excerpt":116,"excerpt_id":117,"has_translation":12,"published_at":118,"reading_time":79,"hidden_from_listing":15,"tags":119,"thumbnail_image":21,"og_image":123,"series":124,"authors":125},114,"One Payment, Four Ledger Rows: An Idempotency Lab","Saldo Naik Empat Kali untuk Satu Pembayaran","saldo-naik-empat-kali-untuk-satu-pembayaran","The code is not wrong and the gateway is not wrong, yet one payment shows up four times in the ledger. Guess why, then make `make check` green in the lab.","Kodenya nggak salah, dan gateway-nya juga nggak salah. Tapi satu pembayaran muncul empat kali di ledger. Coba tebak kenapa — lalu bikin `make check` hijau di lab-nya.","2026-10-11T13:45:00.000Z",[120,121,122,99,70],"idempotency","webhook","postgres","\u002Fog\u002Fsaldo-naik-empat-kali-untuk-satu-pembayaran.png",{"id":86,"title":69,"title_id":69,"slug":70},[126],{"authors_id":127},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},{"id":129,"title":130,"title_id":131,"slug":132,"excerpt":133,"excerpt_id":134,"has_translation":12,"published_at":135,"reading_time":136,"hidden_from_listing":15,"tags":137,"thumbnail_image":21,"og_image":139,"series":140,"authors":141},116,"The Webhook Arrived Late. The Reconciliation Job Had Already Run.","Webhook-nya Telat, Job Rekonsiliasinya Keburu Jalan","webhook-telat-job-rekonsiliasi-keburu-jalan","The webhook path is already idempotent. Then a morning reconciliation job appears, and one payment gets recorded twice from two paths that are both right. Guess where the key is wrong.","Jalur webhook sudah idempotent. Lalu ada job rekonsiliasi pagi, dan satu pembayaran tercatat dua kali dari dua jalur yang dua-duanya benar. Coba tebak kuncinya salah di mana.","2026-10-11T13:44:00.000Z",3,[120,138,121,122,70],"reconciliation","\u002Fog\u002Fwebhook-telat-job-rekonsiliasi-keburu-jalan.png",{"id":86,"title":69,"title_id":69,"slug":70},[142],{"authors_id":143},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},{"id":74,"title":145,"title_id":75,"slug":76,"excerpt":146,"excerpt_id":77,"has_translation":12,"published_at":78,"reading_time":79,"hidden_from_listing":15,"tags":80,"thumbnail_image":21,"og_image":84,"series":147,"authors":148},"Every Receipt in This Café Lists the Same Order","Every table's receipt holds the same items, and the total is Rp0 — even though each table ordered exactly one thing. Guess why, then make `make check` green. A Python scope and mutation lab.",{"id":86,"title":69,"title_id":69,"slug":70},[149],{"authors_id":150},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},{"id":152,"title":153,"title_id":154,"slug":155,"excerpt":156,"excerpt_id":157,"has_translation":12,"published_at":78,"reading_time":79,"hidden_from_listing":15,"tags":158,"thumbnail_image":21,"og_image":161,"series":162,"authors":163},122,"A Script That Cannot Say Where It Went Wrong","Script yang Nggak Bisa Bilang di Mana Salahnya","script-yang-tidak-bilang-di-mana-salahnya","A script that stops at the first bad line reports only part of the problem. This is what shows up on screen right now.","Script yang berhenti di baris jelek pertama cuma melaporkan sebagian masalah yang ada. Ini yang muncul di layar sekarang.",[81,159,160,70],"error-handling","validasi","\u002Fog\u002Fscript-yang-tidak-bilang-di-mana-salahnya.png",{"id":86,"title":69,"title_id":69,"slug":70},[164],{"authors_id":165},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},[],[168],{"authors_id":169},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},"id",[172,170],"en","\nMalam yang ramai di sebuah kafe kecil. Barista buka tiga meja, dan tiap meja pesan satu barang saja: meja 1 pesan kopi, meja 3 pesan teh, meja 5 pesan roti. Sesederhana itu.\n\nSampai barista mencetak notanya. **Nota tiap meja isinya sama semua** — kopi, teh, roti, ketiganya. Padahal tiap meja cuma pesan satu barang. Dan tiap nota bilang totalnya **Rp0**. Rekap kas harian di ujung hari juga Rp0, padahal seharian jualan.\n\nNggak ada satu pun pesan *error* di layar. Nggak ada program yang berhenti mendadak. Aplikasinya jalan sampai selesai, dan kalau kamu buka satu-satu fungsinya lalu baca dari atas, tiap fungsi kelihatan masuk akal. Ada komentar yang menjelaskan niat penulis aslinya, dan niatnya bagus: satu fungsi bilang pesanannya mulai kosong, fungsi lain bilang totalnya dihitung dari item yang sudah masuk. Niat yang bagus, hasil yang salah — itu kombinasi yang paling bikin kesal.\n\n## Angka yang benar-benar keluar sekarang\n\n| | yang terjadi |\n|---|---|\n| tiga meja (1, 3, 5), masing-masing pesan satu barang | tiap nota isinya **3 item yang sama**: `kopi x2, teh x1, roti x1` |\n| total nota tiap meja | **Rp0** (harusnya meja 1 Rp30.000, meja 3 Rp8.000, meja 5 Rp12.000) |\n| kas harian setelah tiga meja jualan | **Rp0** (harusnya Rp50.000) |\n| `make check`, 6 pemeriksaan | **6 dari 6** merah |\n\nBaris pertama itu yang paling menipu. Pesanannya jelas beda-beda: meja 1 kopi, meja 3 teh, meja 5 roti. Tapi begitu masuk ke nota, ketiganya jadi satu daftar yang sama. Nggak ada satu meja pun yang notanya berisi pesanannya sendiri.\n\n`make check` membelah enam pemeriksaan itu jadi tiga kelompok, dan tiap kelompok gagal dengan caranya sendiri. Dua yang pertama soal isi nota: meja yang seharusnya masih kosong ternyata ikut keisi (meja 3 dan 5), dan dua meja yang pesan menu beda (meja 7 dan 9) berakhir dengan isi yang kembar. Dua berikutnya soal total: satu nota harusnya Rp38.000 tapi tertulis Rp0, satu lagi harusnya Rp45.000, hasilnya sama saja. Dua yang terakhir soal kas: satu meja jualan seharusnya menaikkan kas Rp32.000, yang terjadi kasnya naik Rp0. Enam gejala, tiga kelompok, dan masing-masing kelompok menunjuk ke tempat yang beda.\n\n## Misi kamu\n\nBikin `make check` hijau. Enam pemeriksaan (A1, A2, B1, B2, C1, C2) dibagi ke tiga kelompok, dan tiap kelompok menguji satu syarat:\n\n1. **Tiap nota cuma punya isi pesanan meja itu.** Meja yang belum pesan apa-apa notanya tetap kosong, dan dua meja yang pesan menu beda nggak boleh saling ketuker.\n2. **Total nota dihitung dari itemnya.** Jumlah *item* dan *qty*-nya ikut dikalikan, bukan sekadar dibiarkan Rp0.\n3. **Kas harian bertambah sebesar yang benar-benar terjual.** Jual Rp32.000, kas naik Rp32.000.\n\nBoleh mengubah apa saja di satu file itu, asal tiga fungsinya tetap bisa dipanggil dengan cara yang sama seperti sekarang. Dan sebelum kamu buka editornya, ada satu hal yang bikin lab ini beda dari tiga lab sebelumnya.\n\n## Kenapa lab ini nggak butuh Docker\n\nTiga lab sebelumnya di repo ini berjalan di atas FastAPI, Postgres, dan Docker. Lab ini bukan. Semua kodenya Python 3 standar, satu file, tanpa `pip install` dan tanpa database. Alasannya sederhana: kalau kamu harus menyiapkan Docker dulu sebelum bisa mulai belajar, kamu berhenti di langkah nol. Di sini satu-satunya yang kamu butuhkan adalah `python3`, yang sudah ada di hampir semua mesin. Jadi persiapanmu cuma satu: membuka terminal.\n\n## Di mana mulainya\n\nDua perintah, dan satu file untuk kamu baca:\n\n```bash\nmake demo    # ceritanya, bukan angkanya\nmake check   # lihat sendiri pemeriksaan mana yang merah\n```\n\nTerus buka `kasir.py` dan baca dari atas. Tiap fungsi punya komentar yang menjelaskan niat penulisnya. Tugasmu bukan mencari baris yang kelihatan jelek — tugasmu mencari kenapa niat yang bagus tetap menghasilkan nota yang salah.\n\n## Kenapa ini ada\n\nKebanyakan orang belajar \"fungsi\" dari contoh yang cuma dipanggil sekali. Bikin sekali, panggil sekali, cetak, selesai — dan programnya kelihatan jalan. Di kenyataan, fungsi justru dipanggil berkali-kali: meja yang satu, lalu meja yang lain, lalu besok lagi. Ada dua hal yang cuma muncul waktu sebuah fungsi dipakai lebih dari sekali, dan dua-duanya jarang diajarkan bareng contoh pertamamu. Itu sebabnya lab ini ada.\n\n## Batas lab ini\n\n- **Ini bukan pelajaran tentang *class*.** Semua *state* di sini cuma *dict* dan nilai biasa. Di dunia nyata, masalah bentuk ini sering diselesaikan dengan class — tapi *class* bukan obat ajaib. Dia cuma cara lain untuk pertanyaan yang sama: benda mana yang sedang kamu pegang, dan mana yang kamu ubah.\n- **Semua yang rusak di sini rusak tanpa satu pun pesan error.** Kalau selama ini kamu menunggu layar merah buat tahu ada yang salah, lab ini melatih kebalikannya: mencurigai kode yang jalan mulus.\n\n## Pertanyaannya\n\nSebelum kamu menyentuh `kasir.py`, jawab ini dulu: **kenapa meja yang belum pesan apa-apa ikut dapat isi pesanan meja lain?** Dia bukan meja yang sama, notanya bukan nota yang sama, tapi isinya kembar.\n\nPertanyaan kedua: kalau tiap fungsi kelihatan benar saat dibaca sendiri-sendiri, di mana persisnya satu meja \"menitipkan\" datanya ke meja lain? Dan yang terakhir, yang paling menentukan: **kalau kamu cuma memperbaiki satu tempat, apakah cukup?** Ada lebih dari satu sebab di balik tiga gejala ini, dan masing-masing punya obat yang beda. Jawaban panjangnya ada di [artikel jawabannya](\u002Fp\u002Fjawaban-nilai-default-cuma-dibuat-sekali).\n","- Tiga meja pesan satu barang, tapi tiap nota isinya sama dan totalnya Rp0.\n- Nggak ada error, dan tiap fungsi kelihatan benar kalau dibaca sendiri-sendiri.\n- Enam pemeriksaan `make check` merah, terbagi ke tiga kelompok yang gagal dengan cara berbeda.\n- Memperbaiki satu tempat saja nggak cukup: sebabnya lebih dari satu.\n- Pertanyaannya bukan \"di mana bugnya\", tapi benda mana yang sebenarnya diubah.","\u003Cp>Malam yang ramai di sebuah kafe kecil. Barista buka tiga meja, dan tiap meja pesan satu barang saja: meja 1 pesan kopi, meja 3 pesan teh, meja 5 pesan roti. Sesederhana itu.\u003C\u002Fp>\n\u003Cp>Sampai barista mencetak notanya. \u003Cstrong>Nota tiap meja isinya sama semua\u003C\u002Fstrong> — kopi, teh, roti, ketiganya. Padahal tiap meja cuma pesan satu barang. Dan tiap nota bilang totalnya \u003Cstrong>Rp0\u003C\u002Fstrong>. Rekap kas harian di ujung hari juga Rp0, padahal seharian jualan.\u003C\u002Fp>\n\u003Cp>Nggak ada satu pun pesan \u003Cem>error\u003C\u002Fem> di layar. Nggak ada program yang berhenti mendadak. Aplikasinya jalan sampai selesai, dan kalau kamu buka satu-satu fungsinya lalu baca dari atas, tiap fungsi kelihatan masuk akal. Ada komentar yang menjelaskan niat penulis aslinya, dan niatnya bagus: satu fungsi bilang pesanannya mulai kosong, fungsi lain bilang totalnya dihitung dari item yang sudah masuk. Niat yang bagus, hasil yang salah — itu kombinasi yang paling bikin kesal.\u003C\u002Fp>\n\u003Ch2 id=\"angka-yang-benar-benar-keluar-sekarang\">Angka yang benar-benar keluar sekarang\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>yang terjadi\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>tiga meja (1, 3, 5), masing-masing pesan satu barang\u003C\u002Ftd>\n\u003Ctd>tiap nota isinya \u003Cstrong>3 item yang sama\u003C\u002Fstrong>: \u003Ccode>kopi x2, teh x1, roti x1\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>total nota tiap meja\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>Rp0\u003C\u002Fstrong> (harusnya meja 1 Rp30.000, meja 3 Rp8.000, meja 5 Rp12.000)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>kas harian setelah tiga meja jualan\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>Rp0\u003C\u002Fstrong> (harusnya Rp50.000)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>make check\u003C\u002Fcode>, 6 pemeriksaan\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>6 dari 6\u003C\u002Fstrong> merah\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>Baris pertama itu yang paling menipu. Pesanannya jelas beda-beda: meja 1 kopi, meja 3 teh, meja 5 roti. Tapi begitu masuk ke nota, ketiganya jadi satu daftar yang sama. Nggak ada satu meja pun yang notanya berisi pesanannya sendiri.\u003C\u002Fp>\n\u003Cp>\u003Ccode>make check\u003C\u002Fcode> membelah enam pemeriksaan itu jadi tiga kelompok, dan tiap kelompok gagal dengan caranya sendiri. Dua yang pertama soal isi nota: meja yang seharusnya masih kosong ternyata ikut keisi (meja 3 dan 5), dan dua meja yang pesan menu beda (meja 7 dan 9) berakhir dengan isi yang kembar. Dua berikutnya soal total: satu nota harusnya Rp38.000 tapi tertulis Rp0, satu lagi harusnya Rp45.000, hasilnya sama saja. Dua yang terakhir soal kas: satu meja jualan seharusnya menaikkan kas Rp32.000, yang terjadi kasnya naik Rp0. Enam gejala, tiga kelompok, dan masing-masing kelompok menunjuk ke tempat yang beda.\u003C\u002Fp>\n\u003Ch2 id=\"misi-kamu\">Misi kamu\u003C\u002Fh2>\n\u003Cp>Bikin \u003Ccode>make check\u003C\u002Fcode> hijau. Enam pemeriksaan (A1, A2, B1, B2, C1, C2) dibagi ke tiga kelompok, dan tiap kelompok menguji satu syarat:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Tiap nota cuma punya isi pesanan meja itu.\u003C\u002Fstrong> Meja yang belum pesan apa-apa notanya tetap kosong, dan dua meja yang pesan menu beda nggak boleh saling ketuker.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Total nota dihitung dari itemnya.\u003C\u002Fstrong> Jumlah \u003Cem>item\u003C\u002Fem> dan \u003Cem>qty\u003C\u002Fem>-nya ikut dikalikan, bukan sekadar dibiarkan Rp0.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Kas harian bertambah sebesar yang benar-benar terjual.\u003C\u002Fstrong> Jual Rp32.000, kas naik Rp32.000.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Boleh mengubah apa saja di satu file itu, asal tiga fungsinya tetap bisa dipanggil dengan cara yang sama seperti sekarang. Dan sebelum kamu buka editornya, ada satu hal yang bikin lab ini beda dari tiga lab sebelumnya.\u003C\u002Fp>\n\u003Ch2 id=\"kenapa-lab-ini-nggak-butuh-docker\">Kenapa lab ini nggak butuh Docker\u003C\u002Fh2>\n\u003Cp>Tiga lab sebelumnya di repo ini berjalan di atas FastAPI, Postgres, dan Docker. Lab ini bukan. Semua kodenya Python 3 standar, satu file, tanpa \u003Ccode>pip install\u003C\u002Fcode> dan tanpa database. Alasannya sederhana: kalau kamu harus menyiapkan Docker dulu sebelum bisa mulai belajar, kamu berhenti di langkah nol. Di sini satu-satunya yang kamu butuhkan adalah \u003Ccode>python3\u003C\u002Fcode>, yang sudah ada di hampir semua mesin. Jadi persiapanmu cuma satu: membuka terminal.\u003C\u002Fp>\n\u003Ch2 id=\"di-mana-mulainya\">Di mana mulainya\u003C\u002Fh2>\n\u003Cp>Dua perintah, dan satu file untuk kamu baca:\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">BASH\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"make%20demo%20%20%20%20%23%20ceritanya%2C%20bukan%20angkanya%0Amake%20check%20%20%20%23%20lihat%20sendiri%20pemeriksaan%20mana%20yang%20merah\" title=\"Salin kode\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">make\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> demo\u003C\u002Fspan>\u003Cspan style=\"color:#6A737D\">    # ceritanya, bukan angkanya\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">make\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> check\u003C\u002Fspan>\u003Cspan style=\"color:#6A737D\">   # lihat sendiri pemeriksaan mana yang merah\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Terus buka \u003Ccode>kasir.py\u003C\u002Fcode> dan baca dari atas. Tiap fungsi punya komentar yang menjelaskan niat penulisnya. Tugasmu bukan mencari baris yang kelihatan jelek — tugasmu mencari kenapa niat yang bagus tetap menghasilkan nota yang salah.\u003C\u002Fp>\n\u003Ch2 id=\"kenapa-ini-ada\">Kenapa ini ada\u003C\u002Fh2>\n\u003Cp>Kebanyakan orang belajar &quot;fungsi&quot; dari contoh yang cuma dipanggil sekali. Bikin sekali, panggil sekali, cetak, selesai — dan programnya kelihatan jalan. Di kenyataan, fungsi justru dipanggil berkali-kali: meja yang satu, lalu meja yang lain, lalu besok lagi. Ada dua hal yang cuma muncul waktu sebuah fungsi dipakai lebih dari sekali, dan dua-duanya jarang diajarkan bareng contoh pertamamu. Itu sebabnya lab ini ada.\u003C\u002Fp>\n\u003Ch2 id=\"batas-lab-ini\">Batas lab ini\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Ini bukan pelajaran tentang \u003Cem>class\u003C\u002Fem>.\u003C\u002Fstrong> Semua \u003Cem>state\u003C\u002Fem> di sini cuma \u003Cem>dict\u003C\u002Fem> dan nilai biasa. Di dunia nyata, masalah bentuk ini sering diselesaikan dengan class — tapi \u003Cem>class\u003C\u002Fem> bukan obat ajaib. Dia cuma cara lain untuk pertanyaan yang sama: benda mana yang sedang kamu pegang, dan mana yang kamu ubah.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Semua yang rusak di sini rusak tanpa satu pun pesan error.\u003C\u002Fstrong> Kalau selama ini kamu menunggu layar merah buat tahu ada yang salah, lab ini melatih kebalikannya: mencurigai kode yang jalan mulus.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"pertanyaannya\">Pertanyaannya\u003C\u002Fh2>\n\u003Cp>Sebelum kamu menyentuh \u003Ccode>kasir.py\u003C\u002Fcode>, jawab ini dulu: \u003Cstrong>kenapa meja yang belum pesan apa-apa ikut dapat isi pesanan meja lain?\u003C\u002Fstrong> Dia bukan meja yang sama, notanya bukan nota yang sama, tapi isinya kembar.\u003C\u002Fp>\n\u003Cp>Pertanyaan kedua: kalau tiap fungsi kelihatan benar saat dibaca sendiri-sendiri, di mana persisnya satu meja &quot;menitipkan&quot; datanya ke meja lain? Dan yang terakhir, yang paling menentukan: \u003Cstrong>kalau kamu cuma memperbaiki satu tempat, apakah cukup?\u003C\u002Fstrong> Ada lebih dari satu sebab di balik tiga gejala ini, dan masing-masing punya obat yang beda. Jawaban panjangnya ada di \u003Ca href=\"\u002Fp\u002Fjawaban-nilai-default-cuma-dibuat-sekali\">artikel jawabannya\u003C\u002Fa>.\u003C\u002Fp>\n","\u003Cul>\n\u003Cli>Tiga meja pesan satu barang, tapi tiap nota isinya sama dan totalnya Rp0.\u003C\u002Fli>\n\u003Cli>Nggak ada error, dan tiap fungsi kelihatan benar kalau dibaca sendiri-sendiri.\u003C\u002Fli>\n\u003Cli>Enam pemeriksaan \u003Ccode>make check\u003C\u002Fcode> merah, terbagi ke tiga kelompok yang gagal dengan cara berbeda.\u003C\u002Fli>\n\u003Cli>Memperbaiki satu tempat saja nggak cukup: sebabnya lebih dari satu.\u003C\u002Fli>\n\u003Cli>Pertanyaannya bukan &quot;di mana bugnya&quot;, tapi benda mana yang sebenarnya diubah.\u003C\u002Fli>\n\u003C\u002Ful>\n",[178,196,209,222,235,256],{"id":179,"title":180,"title_id":21,"slug":181,"excerpt":182,"excerpt_id":21,"has_translation":15,"published_at":183,"reading_time":184,"hidden_from_listing":15,"tags":185,"thumbnail_image":21,"og_image":187,"series":188,"authors":193},133,"Python Strings: the Badge That Only Worked for One Name","python-strings","The badge script printed the right name for the person who wrote it and the wrong name for everybody else. Slicing counts characters, and a name is words.","2026-10-28T13:00:00.000Z",6,[81,186],"string","\u002Fog\u002Fpython-strings.png",{"id":189,"title":190,"title_id":191,"slug":192},30,"Collections and Strings","Koleksi dan String","python-collections",[194],{"authors_id":195},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},{"id":197,"title":198,"title_id":21,"slug":199,"excerpt":200,"excerpt_id":21,"has_translation":15,"published_at":201,"reading_time":184,"hidden_from_listing":15,"tags":202,"thumbnail_image":21,"og_image":204,"series":205,"authors":206},132,"Python Tuples: Why All Three Points Are the Same Point","python-tuples","Three points walk into a loop and come out identical. The list was never copied three times; the name was. A tuple makes that bug impossible.","2026-10-28T12:00:00.000Z",[81,203],"tuple","\u002Fog\u002Fpython-tuples.png",{"id":189,"title":190,"title_id":191,"slug":192},[207],{"authors_id":208},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},{"id":210,"title":211,"title_id":21,"slug":212,"excerpt":213,"excerpt_id":21,"has_translation":15,"published_at":214,"reading_time":184,"hidden_from_listing":15,"tags":215,"thumbnail_image":21,"og_image":217,"series":218,"authors":219},131,"Python Sets: the Duplicate That Keeps Coming Back","python-sets","A de-duplication loop that works on clean data leaves four tags where there should be two. The bug is case, and the fix is a `set`.","2026-10-28T11:00:00.000Z",[81,216],"set","\u002Fog\u002Fpython-sets.png",{"id":189,"title":190,"title_id":191,"slug":192},[220],{"authors_id":221},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},{"id":223,"title":224,"title_id":21,"slug":225,"excerpt":226,"excerpt_id":21,"has_translation":15,"published_at":227,"reading_time":184,"hidden_from_listing":15,"tags":228,"thumbnail_image":21,"og_image":230,"series":231,"authors":232},130,"Python Dictionaries: the Key That Isn't There","python-dictionary","A word counter dies the first time it meets a word twice. The fix is one line; what takes longer is knowing which of a dict's three lookups to reach for.","2026-10-28T10:00:00.000Z",[81,229],"dictionary","\u002Fog\u002Fpython-dictionary.png",{"id":189,"title":190,"title_id":191,"slug":192},[233],{"authors_id":234},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},{"id":236,"title":237,"title_id":238,"slug":239,"excerpt":240,"excerpt_id":241,"has_translation":12,"published_at":242,"reading_time":14,"hidden_from_listing":15,"tags":243,"thumbnail_image":21,"og_image":250,"series":251,"authors":253},129,"So Your RAG Stops Hitting 504: Split the Query Path From Ingestion","Biar RAG nggak kena 504: pisahin jalur query dan ingestion","rag-pisah-jalur-query-dan-ingestion","One RAG service that both parses uploads and answers queries will fall over the moment someone uploads a 200-page PDF. The cure is two paths and one queue.","Satu service RAG yang merangkap parsing dan menjawab kueri bakal tumbang pas ada yang upload PDF 200 halaman. Obatnya dua jalur dan satu queue.","2026-10-22T01:30:00.000Z",[244,245,246,247,248,249],"rag","cloud-run","cloud-tasks","gcp","serverless","arsitektur","\u002Fog\u002Frag-pisah-jalur-query-dan-ingestion.png",{"id":252,"title":35,"title_id":36,"slug":37},8,[254],{"authors_id":255},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},{"id":257,"title":258,"title_id":259,"slug":260,"excerpt":261,"excerpt_id":262,"has_translation":12,"published_at":263,"reading_time":184,"hidden_from_listing":15,"tags":264,"thumbnail_image":21,"og_image":271,"series":272,"authors":273},128,"Linear or Graph: How to Run Many Agents Without an Eternal Loop","Linear atau graf: cara mengatur banyak agent tanpa loop abadi","multi-agen-linear-atau-grafik","One agent with twenty tools gets messy fast. Once several agents revise each other, you need a graph that allows cycles and a number that makes it stop.","Satu agent dengan dua puluh tool gampang kacau. Begitu beberapa agent saling revisi, kamu butuh graf yang mendukung cycle dan angka yang membuatnya berhenti.","2026-10-21T16:05:00.000Z",[265,266,267,268,269,270],"multi-agent","langgraph","autogen","orkestrasi","checkpoint","llm","\u002Fog\u002Fmulti-agen-linear-atau-grafik.png",{"id":252,"title":35,"title_id":36,"slug":37},[274],{"authors_id":275},{"id":107,"name":108,"slug":109,"role":110,"profile_picture":21},[277,281,284,287,290,293,296],{"text":278,"depth":279,"id":280},"Angka yang benar-benar keluar sekarang",2,"angka-yang-benar-benar-keluar-sekarang",{"text":282,"depth":279,"id":283},"Misi kamu","misi-kamu",{"text":285,"depth":279,"id":286},"Kenapa lab ini nggak butuh Docker","kenapa-lab-ini-nggak-butuh-docker",{"text":288,"depth":279,"id":289},"Di mana mulainya","di-mana-mulainya",{"text":291,"depth":279,"id":292},"Kenapa ini ada","kenapa-ini-ada",{"text":294,"depth":279,"id":295},"Batas lab ini","batas-lab-ini",{"text":297,"depth":279,"id":298},"Pertanyaannya","pertanyaannya",1793336439042]