<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[DOT Intern - Medium]]></title>
        <description><![CDATA[Artikel dari para peserta DOT Internship - Medium]]></description>
        <link>https://medium.com/dot-intern?source=rss----20935cb0cc37---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>DOT Intern - Medium</title>
            <link>https://medium.com/dot-intern?source=rss----20935cb0cc37---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 15:32:41 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/dot-intern" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Auto-Labeling Dataset Object Detection dengan NVIDIA LocateAnything: Dari Prompt ke Format YOLO]]></title>
            <link>https://medium.com/dot-intern/auto-labeling-dataset-object-detection-dengan-nvidia-locateanything-dari-prompt-ke-format-yolo-408dbdaf28b3?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/408dbdaf28b3</guid>
            <dc:creator><![CDATA[Taqiyudinmiftah ]]></dc:creator>
            <pubDate>Sat, 25 Jul 2026 04:01:03 GMT</pubDate>
            <atom:updated>2026-07-25T04:01:02.845Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Meni pekerjaan manual menjadi pipeline berbasis vision-language model yang tetap dapat diaudit.</em></p><p>Membangun model object detection hampir selalu dimulai dari pekerjaan yang sama: menggambar bounding box, memilih label, lalu mengulangi proses tersebut untuk ratusan atau ribuan gambar.</p><p>Masalahnya, proses labeling manual bukan hanya memakan waktu. Konsistensi anotasi juga mudah menurun ketika dataset semakin besar, jumlah kelas bertambah, atau beberapa anotator memiliki interpretasi berbeda.</p><p>Salah satu pendekatan yang kini dapat digunakan untuk mempercepat tahap tersebut adalah <strong>auto-labeling dengan vision-language model</strong>. Kita cukup memberikan gambar dan daftar objek yang dicari, kemudian model menghasilkan nama kelas beserta koordinat bounding box.</p><p>Dalam artikel ini, kita akan membangun pipeline auto-labeling menggunakan <strong>NVIDIA LocateAnything-3B</strong>, lalu mengubah hasilnya menjadi anotasi yang dapat digunakan untuk melatih model YOLO.</p><h3>Apa Itu LocateAnything?</h3><p>LocateAnything adalah vision-language model untuk melakukan visual grounding dan object localization berdasarkan instruksi bahasa alami. Model ini dapat digunakan untuk beberapa pekerjaan, antara lain:</p><ul><li>open-set dan long-tail object detection;</li><li>dense multi-object detection;</li><li>phrase grounding;</li><li>GUI element grounding;</li><li>OCR dan document-layout localization;</li><li>point-based localization;</li><li>automated dataset annotation.</li></ul><p>LocateAnything menerima kombinasi gambar dan prompt teks, lalu menghasilkan label semantik serta koordinat objek yang ditemukan. rompt sederhananya:</p><pre>Locate all the instances that match the following description:<br>person, motorcycle, helmet.</pre><p>Respons model akan memiliki struktur seperti berikut:</p><pre>&lt;ref&gt;person&lt;/ref&gt;&lt;box&gt;&lt;102&gt;&lt;115&gt;&lt;434&gt;&lt;972&gt;&lt;/box&gt;<br>&lt;ref&gt;helmet&lt;/ref&gt;&lt;box&gt;&lt;184&gt;&lt;81&gt;&lt;292&gt;&lt;221&gt;&lt;/box&gt;</pre><p>Koordinat tersebut bukan koordinat piksel langsung. LocateAnything menggunakan bilangan bulat dalam rentang 0–1000, yang dapat dibagi dengan 1000 untuk memperoleh koordinat relatif terhadap lebar dan tinggi gambar.</p><pre>&lt;x1&gt;&lt;y1&gt;&lt;x2&gt;&lt;y2&gt;</pre><p>dapat dikonversi menjadi:</p><pre>x1_pixel = x1 / 1000 × image_width<br>y1_pixel = y1 / 1000 × image_height<br>x2_pixel = x2 / 1000 × image_width<br>y2_pixel = y2 / 1000 × image_height</pre><p>Format relatif ini memudahkan kita mengubah hasil model ke format YOLO tanpa harus terlebih dahulu menghitung koordinat piksel.</p><h3>Mengapa LocateAnything Menarik untuk Auto-Labeling?</h3><p>Sebagian besar vision-language model generatif menghasilkan koordinat secara berurutan: nilai x1, kemudian y1, x2, dan y2.</p><p>LocateAnything memperkenalkan metode bernama <strong>Parallel Box Decoding atau PBD</strong>. Melalui PBD, sebuah bounding box diperlakukan sebagai satu unit geometris dan seluruh koordinatnya diprediksi secara paralel.</p><p>Pendekatan tersebut dirancang untuk meningkatkan kecepatan decoding sekaligus menjaga konsistensi struktur bounding box. Evaluasi NVIDIA menggunakan satu GPU H100, LocateAnything dilaporkan mencapai 12,7 boxes per second atau BPS. Angka ini lebih dari sepuluh kali throughput Qwen3-VL dan sekitar 2,5 kali throughput Rex-Omni dalam pengujian yang dilaporkan oleh tim peneliti. Perlu diperhatikan bahwa BPS mengukur kecepatan decoding box, bukan jumlah gambar end-to-end per detik. ything memiliki tiga mode inference:</p><h3>Fast Mode</h3><p>Mode fast hanya menggunakan parallel decoding. Mode ini memberikan throughput tertinggi dan cocok untuk:</p><ul><li>eksperimen cepat;</li><li>dataset dengan adegan sederhana;</li><li>initial pre-labeling;</li><li>aplikasi yang sensitif terhadap latency.</li></ul><h3>Slow Mode</h3><p>Mode slow menghasilkan koordinat secara autoregresif. Kecepatannya lebih rendah, tetapi menjadi mode yang paling stabil untuk:</p><ul><li>final labeling;</li><li>dataset curation;</li><li>offline evaluation;</li><li>kasus dengan objek padat atau saling berdekatan.</li></ul><h3>Hybrid Mode</h3><p>Mode hybrid memulai inference menggunakan parallel decoding. Ketika menemukan struktur yang tidak valid atau lokasi yang ambigu, model beralih sementara ke autoregressive decoding.</p><p>Mode ini menjadi pilihan default yang masuk akal karena menawarkan keseimbangan antara kecepatan dan kualitas. peline auto-labeling, strategi yang praktis adalah:</p><pre>Hybrid mode → anotasi awal seluruh dataset<br>Slow mode   → validasi ulang gambar bermasalah<br>Human review → koreksi akhir pada sampel atau kasus ambigu</pre><h3>Arsitektur Pipeline</h3><p>Pipeline yang akan kita bangun memiliki alur berikut:</p><pre>Folder gambar<br>     │<br>     ▼<br>Daftar kelas atau prompt<br>     │<br>     ▼<br>LocateAnything inference<br>     │<br>     ▼<br>Parse &lt;ref&gt; dan &lt;box&gt;<br>     │<br>     ▼<br>Validasi koordinat<br>     │<br>     ├── Simpan respons mentah untuk audit<br>     │<br>     ▼<br>Konversi ke format YOLO<br>     │<br>     ▼<br>Folder labels/*.txt</pre><p>Pada implementasi produksi, sebaiknya jangan langsung menganggap semua output model sebagai ground truth.</p><p>Istilah yang lebih tepat adalah <strong>pre-annotation</strong>: model menyiapkan anotasi awal, sementara sistem validasi dan manusia tetap menentukan kualitas akhirnya.</p><h3>Instalasi LocateAnything</h3><p>Kode resmi LocateAnything tersedia sebagai bagian dari repositori Eagle milik NVIDIA.</p><pre>git clone https://github.com/NVlabs/Eagle.git eagle<br>cd eagle/Embodied</pre><pre>pip install -e .</pre><p>PyTorch perlu dipasang secara terpisah sesuai versi CUDA pada komputer yang digunakan. Repositori resminya menggunakan beberapa dependency spesifik, termasuk Transformers 4.57.1. GPU dapat terdeteksi:</p><pre>python -c &quot;import torch; print(torch.cuda.is_available())&quot;</pre><p>Output yang diharapkan:</p><pre>True</pre><p>Model dapat diunduh secara otomatis dari Hugging Face ketika pertama kali dijalankan:</p><pre>worker = LocateAnythingWorker(&quot;nvidia/LocateAnything-3B&quot;)</pre><p>Karena implementasi resmi memuat custom model code, penggunaan production sebaiknya melakukan pin terhadap model revision dan meninjau kode yang dijalankan sebelum deployment.</p><h3>Format Label YOLO</h3><p>YOLO menggunakan format:</p><pre>class_id x_center y_center width height</pre><p>Semua koordinat berada dalam skala 0–1.</p><p>Sebagai contoh:</p><pre>0 0.517000 0.431000 0.284000 0.526000</pre><p>Jika LocateAnything menghasilkan:</p><pre>x1 = 200<br>y1 = 150<br>x2 = 600<br>y2 = 750</pre><p>maka koordinat relatifnya adalah:</p><pre>x1 = 200 / 1000<br>y1 = 150 / 1000<br>x2 = 600 / 1000<br>y2 = 750 / 1000</pre><p>Konversi ke YOLO:</p><pre>x_center = (x1 + x2) / 2<br>y_center = (y1 + y2) / 2<br>width = x2 - x1<br>height = y2 - y1</pre><p>Karena koordinat LocateAnything sudah dinormalisasi terhadap gambar, kita tidak perlu mengetahui resolusi gambar untuk membuat file YOLO.</p><h3>Implementasi Auto-Labeling</h3><p>Buat file bernama auto_label.py:</p><pre>from __future__ import annotations</pre><pre>import argparse<br>import json<br>import re<br>from pathlib import Path<br>from typing import Iterator</pre><pre>from PIL import Image<br>from locateanything_worker import LocateAnythingWorker<br></pre><pre>MODEL_ID = &quot;nvidia/LocateAnything-3B&quot;</pre><pre>SUPPORTED_EXTENSIONS = {<br>    &quot;.jpg&quot;,<br>    &quot;.jpeg&quot;,<br>    &quot;.png&quot;,<br>    &quot;.bmp&quot;,<br>    &quot;.webp&quot;,<br>}</pre><pre># Sesuaikan dengan objek yang ingin dideteksi.<br>CLASSES = [<br>    &quot;person&quot;,<br>    &quot;motorcycle&quot;,<br>    &quot;helmet&quot;,<br>]</pre><pre>CLASS_TO_ID = {<br>    class_name.lower(): class_id<br>    for class_id, class_name in enumerate(CLASSES)<br>}</pre><pre># Format resmi:<br># &lt;ref&gt;label&lt;/ref&gt;&lt;box&gt;&lt;x1&gt;&lt;y1&gt;&lt;x2&gt;&lt;y2&gt;&lt;/box&gt;<br>DETECTION_PATTERN = re.compile(<br>    r&quot;&lt;ref&gt;(.*?)&lt;/ref&gt;\s*&quot;<br>    r&quot;&lt;box&gt;&lt;(\d+)&gt;&lt;(\d+)&gt;&lt;(\d+)&gt;&lt;(\d+)&gt;&lt;/box&gt;&quot;,<br>    re.IGNORECASE,<br>)<br></pre><pre>def clamp(value: float, minimum: float = 0.0, maximum: float = 1.0) -&gt; float:<br>    &quot;&quot;&quot;Membatasi nilai agar selalu berada dalam rentang yang valid.&quot;&quot;&quot;<br>    return max(minimum, min(value, maximum))<br></pre><pre>def normalize_label(label: str) -&gt; str:<br>    &quot;&quot;&quot;Membersihkan label yang dihasilkan model.&quot;&quot;&quot;<br>    return &quot; &quot;.join(label.strip().lower().split())<br></pre><pre>def parse_detections(answer: str) -&gt; Iterator[dict]:<br>    &quot;&quot;&quot;<br>    Mengubah output LocateAnything menjadi anotasi terstruktur.</pre><pre>    Koordinat model berada dalam rentang 0–1000 dan langsung<br>    dikonversi menjadi koordinat relatif 0–1.<br>    &quot;&quot;&quot;<br>    for match in DETECTION_PATTERN.finditer(answer):<br>        label = normalize_label(match.group(1))</pre><pre>        # Abaikan kelas yang tidak termasuk ontology dataset.<br>        if label not in CLASS_TO_ID:<br>            continue</pre><pre>        x1_raw, y1_raw, x2_raw, y2_raw = map(<br>            int,<br>            match.groups()[1:],<br>        )</pre><pre>        x1 = clamp(x1_raw / 1000.0)<br>        y1 = clamp(y1_raw / 1000.0)<br>        x2 = clamp(x2_raw / 1000.0)<br>        y2 = clamp(y2_raw / 1000.0)</pre><pre>        # Jangan memperbaiki box rusak secara diam-diam.<br>        # Lebih aman mengabaikannya dan mencatat output mentah.<br>        if x2 &lt;= x1 or y2 &lt;= y1:<br>            continue</pre><pre>        yield {<br>            &quot;label&quot;: label,<br>            &quot;class_id&quot;: CLASS_TO_ID[label],<br>            &quot;x1&quot;: x1,<br>            &quot;y1&quot;: y1,<br>            &quot;x2&quot;: x2,<br>            &quot;y2&quot;: y2,<br>        }<br></pre><pre>def detection_to_yolo(detection: dict) -&gt; str:<br>    &quot;&quot;&quot;Mengonversi satu bounding box ke format YOLO.&quot;&quot;&quot;<br>    x1 = detection[&quot;x1&quot;]<br>    y1 = detection[&quot;y1&quot;]<br>    x2 = detection[&quot;x2&quot;]<br>    y2 = detection[&quot;y2&quot;]</pre><pre>    x_center = (x1 + x2) / 2.0<br>    y_center = (y1 + y2) / 2.0<br>    width = x2 - x1<br>    height = y2 - y1</pre><pre>    return (<br>        f&#39;{detection[&quot;class_id&quot;]} &#39;<br>        f&quot;{x_center:.6f} &quot;<br>        f&quot;{y_center:.6f} &quot;<br>        f&quot;{width:.6f} &quot;<br>        f&quot;{height:.6f}&quot;<br>    )<br></pre><pre>def collect_images(image_directory: Path) -&gt; list[Path]:<br>    &quot;&quot;&quot;Mengambil semua gambar yang didukung secara rekursif.&quot;&quot;&quot;<br>    return sorted(<br>        path<br>        for path in image_directory.rglob(&quot;*&quot;)<br>        if path.is_file() and path.suffix.lower() in SUPPORTED_EXTENSIONS<br>    )<br></pre><pre>def run_auto_labeling(<br>    image_directory: Path,<br>    label_directory: Path,<br>    audit_file: Path,<br>    generation_mode: str,<br>) -&gt; None:<br>    label_directory.mkdir(parents=True, exist_ok=True)<br>    audit_file.parent.mkdir(parents=True, exist_ok=True)</pre><pre>    images = collect_images(image_directory)</pre><pre>    if not images:<br>        raise RuntimeError(<br>            f&quot;Tidak ditemukan gambar di {image_directory.resolve()}&quot;<br>        )</pre><pre>    print(f&quot;Memuat model {MODEL_ID} ...&quot;)</pre><pre>    worker = LocateAnythingWorker(MODEL_ID)</pre><pre>    with audit_file.open(&quot;w&quot;, encoding=&quot;utf-8&quot;) as audit:<br>        for index, image_path in enumerate(images, start=1):<br>            try:<br>                image = Image.open(image_path).convert(&quot;RGB&quot;)</pre><pre>                result = worker.detect(<br>                    image,<br>                    CLASSES,<br>                    generation_mode=generation_mode,<br>                    max_new_tokens=8192,<br>                    verbose=False,<br>                )</pre><pre>                raw_answer = str(result[&quot;answer&quot;])<br>                detections = list(parse_detections(raw_answer))</pre><pre>                yolo_lines = [<br>                    detection_to_yolo(detection)<br>                    for detection in detections<br>                ]</pre><pre>                relative_path = image_path.relative_to(image_directory)<br>                output_path = (<br>                    label_directory / relative_path<br>                ).with_suffix(&quot;.txt&quot;)</pre><pre>                output_path.parent.mkdir(parents=True, exist_ok=True)<br>                output_path.write_text(<br>                    &quot;\n&quot;.join(yolo_lines),<br>                    encoding=&quot;utf-8&quot;,<br>                )</pre><pre>                # Respons mentah disimpan agar hasil dapat diperiksa ulang.<br>                audit_record = {<br>                    &quot;image&quot;: str(image_path),<br>                    &quot;label_file&quot;: str(output_path),<br>                    &quot;generation_mode&quot;: generation_mode,<br>                    &quot;classes&quot;: CLASSES,<br>                    &quot;detection_count&quot;: len(detections),<br>                    &quot;detections&quot;: detections,<br>                    &quot;raw_answer&quot;: raw_answer,<br>                }</pre><pre>                audit.write(<br>                    json.dumps(audit_record, ensure_ascii=False) + &quot;\n&quot;<br>                )</pre><pre>                print(<br>                    f&quot;[{index}/{len(images)}] &quot;<br>                    f&quot;{image_path.name}: &quot;<br>                    f&quot;{len(detections)} objek&quot;<br>                )</pre><pre>            except Exception as error:<br>                error_record = {<br>                    &quot;image&quot;: str(image_path),<br>                    &quot;error&quot;: str(error),<br>                }</pre><pre>                audit.write(<br>                    json.dumps(error_record, ensure_ascii=False) + &quot;\n&quot;<br>                )</pre><pre>                print(<br>                    f&quot;[{index}/{len(images)}] &quot;<br>                    f&quot;Gagal memproses {image_path.name}: {error}&quot;<br>                )<br></pre><pre>def parse_arguments() -&gt; argparse.Namespace:<br>    parser = argparse.ArgumentParser(<br>        description=(<br>            &quot;Auto-label dataset object detection menggunakan &quot;<br>            &quot;NVIDIA LocateAnything.&quot;<br>        )<br>    )</pre><pre>    parser.add_argument(<br>        &quot;--images&quot;,<br>        type=Path,<br>        required=True,<br>        help=&quot;Folder yang berisi gambar.&quot;,<br>    )</pre><pre>    parser.add_argument(<br>        &quot;--labels&quot;,<br>        type=Path,<br>        required=True,<br>        help=&quot;Folder keluaran label YOLO.&quot;,<br>    )</pre><pre>    parser.add_argument(<br>        &quot;--audit&quot;,<br>        type=Path,<br>        default=Path(&quot;auto_label_audit.jsonl&quot;),<br>        help=&quot;Lokasi file audit JSONL.&quot;,<br>    )</pre><pre>    parser.add_argument(<br>        &quot;--mode&quot;,<br>        choices=[&quot;fast&quot;, &quot;slow&quot;, &quot;hybrid&quot;],<br>        default=&quot;hybrid&quot;,<br>        help=&quot;Mode decoding LocateAnything.&quot;,<br>    )</pre><pre>    return parser.parse_args()<br></pre><pre>if __name__ == &quot;__main__&quot;:<br>    args = parse_arguments()</pre><pre>    run_auto_labeling(<br>        image_directory=args.images,<br>        label_directory=args.labels,<br>        audit_file=args.audit,<br>        generation_mode=args.mode,<br>    )</pre><p>Jalankan program:</p><pre>python auto_label.py \<br>  --images dataset/images/train \<br>  --labels dataset/labels/train \<br>  --audit dataset/auto_label_audit.jsonl \<br>  --mode hybrid</pre><p>Struktur hasilnya akan menjadi:</p><pre>dataset/<br>├── images/<br>│   └── train/<br>│       ├── image_001.jpg<br>│       ├── image_002.jpg<br>│       └── image_003.jpg<br>│<br>├── labels/<br>│   └── train/<br>│       ├── image_001.txt<br>│       ├── image_002.txt<br>│       └── image_003.txt<br>│<br>└── auto_label_audit.jsonl</pre><h3>Mengapa Respons Mentah Harus Disimpan?</h3><p>Dalam pipeline machine learning, hasil anotasi sebaiknya dapat ditelusuri kembali.</p><p>File auto_label_audit.jsonl menyimpan:</p><ul><li>nama gambar;</li><li>mode inference;</li><li>daftar kelas;</li><li>jumlah objek;</li><li>koordinat hasil parsing;</li><li>respons mentah model;</li><li>pesan error jika inference gagal.</li></ul><p>Data tersebut membantu ketika kita menemukan anotasi yang aneh.</p><p>Misalnya, model menghasilkan label kosong atau bounding box yang terlalu kecil. Kita dapat memeriksa apakah kesalahan terjadi pada respons model, parser, atau proses konversi.</p><p>Audit log juga berguna ketika model, prompt, dependency, atau parameter inference berubah.</p><h3>Gunakan Ontology yang Jelas</h3><p>Jangan memberikan daftar kelas yang ambigu.</p><p>Daftar berikut berpotensi menghasilkan label yang tumpang tindih:</p><pre>CLASSES = [<br>    &quot;vehicle&quot;,<br>    &quot;car&quot;,<br>    &quot;sedan&quot;,<br>]</pre><p>Satu sedan dapat dianggap sebagai ketiga kelas tersebut.</p><p>Ontology yang lebih jelas:</p><pre>CLASSES = [<br>    &quot;car&quot;,<br>    &quot;motorcycle&quot;,<br>    &quot;bus&quot;,<br>    &quot;truck&quot;,<br>]</pre><p>Apabila kelas membutuhkan aturan khusus, tambahkan deskripsi ke prompt.</p><p>Contoh:</p><pre>Locate all workers wearing a construction safety helmet.<br>Do not label caps, hats, or motorcycle helmets.</pre><p>Model menerima free-form natural-language description, sehingga prompt dapat memuat konteks visual yang lebih spesifik daripada sekadar nama kelas. ata training dan prompt yang didokumentasikan untuk model ini umumnya menggunakan ekspresi bahasa Inggris yang pendek dan berorientasi pada tugas. Karena itu, prompt berbahasa Inggris biasanya menjadi titik awal yang lebih aman untuk eksperimen. tetap dapat menggunakan nama kelas berbahasa Indonesia. Buat pemisahan antara prompt dan nama label:</p><pre>PROMPT_CLASSES = [<br>    &quot;construction worker&quot;,<br>    &quot;safety helmet&quot;,<br>]</pre><pre>DATASET_CLASSES = [<br>    &quot;pekerja&quot;,<br>    &quot;helm_keselamatan&quot;,<br>]</pre><h3>Jangan Mengandalkan Confidence Threshold</h3><p>Pada object detector konvensional, kita biasanya menerima:</p><pre>class, bounding box, confidence</pre><p>Pada format output resmi LocateAnything yang didokumentasikan, hasil utamanya berupa label semantik dan koordinat terstruktur. Tidak ada skor confidence konvensional untuk setiap bounding box. tu, kita tidak dapat sekadar menulis:</p><pre>if confidence &gt; 0.8:<br>    save_label()</pre><p>Sebagai gantinya, kualitas dapat dikendalikan dengan beberapa mekanisme:</p><ol><li>Memeriksa validitas koordinat.</li><li>Menolak bounding box yang terlalu kecil atau terlalu besar.</li><li>Menjalankan kembali gambar yang mencurigakan dengan mode slow.</li><li>Membandingkan output dari dua variasi prompt.</li><li>Melakukan review manual pada sampel dataset.</li><li>Mengukur precision, recall, dan IoU terhadap subset yang telah diberi ground truth manusia.</li></ol><p>Contoh aturan untuk menolak box sangat kecil:</p><pre>MIN_BOX_AREA = 0.0005</pre><pre>box_area = (<br>    (detection[&quot;x2&quot;] - detection[&quot;x1&quot;])<br>    * (detection[&quot;y2&quot;] - detection[&quot;y1&quot;])<br>)</pre><pre>if box_area &lt; MIN_BOX_AREA:<br>    continue</pre><p>Nilai minimum tersebut tidak universal. Angkanya harus disesuaikan dengan ukuran objek yang memang dibutuhkan oleh aplikasi.</p><h3>Human-in-the-Loop Tetap Diperlukan</h3><p>Auto-labeling tidak berarti menghapus manusia dari proses anotasi.</p><p>Pendekatan yang lebih aman:</p><h3>Tahap 1 — Buat Dataset Pilot</h3><p>Pilih sekitar 100–300 gambar yang mewakili:</p><ul><li>variasi pencahayaan;</li><li>sudut kamera;</li><li>background;</li><li>ukuran objek;</li><li>kondisi occlusion;</li><li>kepadatan objek.</li></ul><p>Label subset tersebut secara manual sebagai validation set.</p><h3>Tahap 2 — Uji Prompt</h3><p>Jalankan beberapa variasi prompt dan bandingkan hasilnya.</p><p>Contoh:</p><pre>helmet</pre><p>dibandingkan dengan:</p><pre>safety helmets worn by construction workers</pre><p>Prompt kedua mungkin menghasilkan presisi lebih tinggi, tetapi dapat melewatkan helm yang sedang diletakkan di meja. Pilihan prompt harus mengikuti definisi label dataset.</p><h3>Tahap 3 — Jalankan Hybrid Mode</h3><p>Gunakan mode hybrid untuk menghasilkan pre-annotation pada seluruh dataset.</p><h3>Tahap 4 — Tandai Kasus Mencurigakan</h3><p>Masukkan gambar ke antrean review ketika:</p><ul><li>tidak ada objek yang ditemukan;</li><li>jumlah box jauh di atas distribusi normal;</li><li>terdapat box berukuran hampir satu gambar penuh;</li><li>box terlalu kecil;</li><li>model menghasilkan label di luar ontology;</li><li>parser menemukan output tidak valid.</li></ul><h3>Tahap 5 — Re-Inference dengan Slow Mode</h3><p>Gambar bermasalah dapat dijalankan ulang:</p><pre>python auto_label.py \<br>  --images review/images \<br>  --labels review/labels \<br>  --audit review/slow_mode_audit.jsonl \<br>  --mode slow</pre><h3>Tahap 6 — Review Manusia</h3><p>Gunakan aplikasi anotasi untuk memeriksa dan mengoreksi hasil akhir sebelum memasukkannya ke training set.</p><p>Dengan pola ini, anotator tidak lagi menggambar seluruh box dari awal. Mereka hanya memvalidasi, menggeser, menghapus, atau menambahkan box yang diperlukan.</p><h3>LocateAnything dan YOLO Memiliki Peran Berbeda</h3><p>LocateAnything bukan pengganti langsung untuk YOLO dalam semua skenario.</p><p>LocateAnything berguna sebagai <strong>generalist model</strong> pada tahap:</p><pre>raw images → pre-annotation</pre><p>YOLO digunakan sebagai <strong>specialist model</strong> pada tahap:</p><pre>verified annotations → training → production inference</pre><p>LocateAnything dapat mencari objek berdasarkan natural-language prompt tanpa terlebih dahulu melatih detector khusus untuk setiap kelas.</p><p>Sebaliknya, setelah dilatih dengan dataset domain-specific, YOLO biasanya lebih cocok untuk inference berulang yang membutuhkan latency rendah dan format output yang konsisten.</p><p>Alur lengkapnya:</p><pre>Kumpulan gambar mentah<br>        │<br>        ▼<br>LocateAnything pre-labeling<br>        │<br>        ▼<br>Validasi otomatis<br>        │<br>        ▼<br>Koreksi manusia<br>        │<br>        ▼<br>Dataset YOLO<br>        │<br>        ▼<br>Training model YOLO<br>        │<br>        ▼<br>Evaluasi dan deployment</pre><h3>Praktik yang Sebaiknya Dihindari</h3><h3>Memproses Seluruh Dataset Tanpa Pilot Test</h3><p>Kesalahan prompt pada 100 gambar masih mudah diperbaiki. Kesalahan yang sama pada 100.000 gambar akan menghasilkan technical debt yang besar.</p><h3>Menerima Semua Kelas dari Model</h3><p>Selalu gunakan class allowlist. Jangan otomatis menambahkan kelas baru hanya karena model menuliskannya.</p><h3>Memperbaiki Koordinat Rusak Secara Diam-Diam</h3><p>Jika model menghasilkan x2 &lt; x1, jangan langsung menukar keduanya tanpa pencatatan. Box tersebut mungkin menandakan output yang tidak stabil.</p><p>Lebih aman menolak box dan memasukkan gambarnya ke antrean review.</p><h3>Mengabaikan Empty Label</h3><p>File anotasi kosong belum tentu berarti gambar tersebut benar-benar tidak memiliki objek. Bisa saja model gagal mendeteksi objek.</p><p>Empty label sebaiknya ikut diperiksa, terutama untuk dataset yang hampir seluruh gambarnya mengandung objek target.</p><h3>Menghapus Raw Output</h3><p>Parser dan aturan konversi dapat berubah. Menyimpan respons mentah memungkinkan kita menghasilkan ulang file anotasi tanpa menjalankan inference GPU kembali.</p><h3>Catatan Lisensi</h3><p>Pada saat artikel ini ditulis, model card resmi LocateAnything-3B menyatakan bahwa model dirilis untuk penggunaan nonkomersial, termasuk penelitian akademik dan nonprofit. Penggunaan komersial tidak diizinkan kecuali oleh NVIDIA dan afiliasinya. tu, baca dan evaluasi lisensi resminya sebelum menggunakan model untuk:</p><ul><li>produk komersial;</li><li>layanan berbayar;</li><li>dataset klien;</li><li>aplikasi internal perusahaan;</li><li>distribusi ulang model atau turunannya.</li></ul><p>Lisensi source code dan lisensi model weights juga dapat memiliki ketentuan yang berbeda.</p><h3>Kesimpulan</h3><p>LocateAnything menunjukkan bagaimana vision-language model dapat digunakan bukan hanya untuk menjelaskan gambar, tetapi juga untuk menghasilkan lokasi objek yang terstruktur.</p><p>Kemampuan menerima natural-language prompt membuatnya menarik untuk proses pre-annotation, terutama ketika:</p><ul><li>jumlah kelas sering berubah;</li><li>terdapat long-tail objects;</li><li>dataset belum memiliki detector khusus;</li><li>anotasi manual terlalu mahal;</li><li>tim membutuhkan bootstrap dataset dengan cepat.</li></ul><p>Namun, keberhasilan auto-labeling tidak hanya bergantung pada kualitas model.</p><p>Pipeline yang baik juga membutuhkan:</p><ul><li>ontology kelas yang tegas;</li><li>prompt yang konsisten;</li><li>validasi koordinat;</li><li>audit log;</li><li>subset ground truth;</li><li>pengukuran kualitas;</li><li>serta human-in-the-loop.</li></ul><p>Dengan pendekatan tersebut, LocateAnything tidak diposisikan sebagai mesin pembuat ground truth tanpa kesalahan, melainkan sebagai <strong>annotation assistant</strong> yang mempercepat pekerjaan manusia.</p><p>Dan untuk banyak proyek computer vision, penghematan terbesar memang bukan berasal dari menghilangkan anotator — melainkan dari memastikan anotator tidak perlu menggambar setiap bounding box dari nol.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=408dbdaf28b3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/auto-labeling-dataset-object-detection-dengan-nvidia-locateanything-dari-prompt-ke-format-yolo-408dbdaf28b3">Auto-Labeling Dataset Object Detection dengan NVIDIA LocateAnything: Dari Prompt ke Format YOLO</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Google Sheets vs Software Akuntansi? Di Tim Finance, Keduanya Justru Saling Melengkapi]]></title>
            <link>https://medium.com/dot-intern/google-sheets-vs-software-akuntansi-di-tim-finance-keduanya-justru-saling-melengkapi-f41af245850b?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/f41af245850b</guid>
            <category><![CDATA[accounting-software]]></category>
            <category><![CDATA[google-sheets]]></category>
            <category><![CDATA[accounting]]></category>
            <category><![CDATA[workflow]]></category>
            <category><![CDATA[finance]]></category>
            <dc:creator><![CDATA[Salsabila]]></dc:creator>
            <pubDate>Fri, 24 Jul 2026 04:01:02 GMT</pubDate>
            <atom:updated>2026-07-24T04:01:02.088Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/756/1*FACiMRDFTjfAcgpuXGb5hw.png" /></figure><p>Banyak orang mengira bahwa ketika perusahaan mulai menggunakan software akuntansi, peran spreadsheet seperti Google Sheets akan langsung hilang. Padahal dalam praktiknya, terutama di lingkungan kerja yang serba cepat, hal itu tidak selalu terjadi.</p><p>Selama menjalani magang di tim finance, saya justru melihat bahwa Google Sheets dan software akuntansi bukanlah dua alat yang saling menggantikan. Keduanya punya peran yang berbeda. Ketika digunakan secara berdampingan, hasilnya bisa jauh lebih efisien.</p><p>Kunci dari manajemen keuangan yang rapi ternyata bukan soal memilih tools mana yang paling unggul, tetapi bagaimana memahami <strong>fungsi masing-masing tools</strong> dan menggunakannya pada tahap kerja yang tepat.</p><p><strong>Dua tools, dua fungsi berbeda</strong></p><p>Sebelum membangun alur kerja yang efisien, tim finance perlu memahami karakter dari masing-masing alat.</p><p><strong>Google Sheets: fleksibel untuk proses yang dinamis</strong></p><p>Google Sheets sangat membantu untuk pekerjaan yang masih berubah-ubah dan membutuhkan penyesuaian cepat. Dalam operasional harian, tools ini cocok digunakan untuk:</p><ul><li><strong>Perhitungan sementara</strong>, misalnya simulasi angka sebelum data difinalisasi.</li><li><strong>Tracker internal</strong>, seperti catatan pengingat pembayaran, monitoring transaksi, atau checklist pekerjaan harian.</li><li><strong>Analisis data mentah</strong>, dengan bantuan formula seperti VLOOKUP, FILTER, atau Pivot Table untuk memilah data dalam jumlah besar.</li></ul><p>Keunggulan utama Google Sheets ada pada fleksibilitasnya. Tim bisa mengedit, menambahkan catatan, dan menyesuaikan format kapan saja sesuai kebutuhan.</p><p><strong>Software akuntansi: terstruktur untuk pencatatan resmi</strong></p><p>Di sisi lain, software akuntansi berperan sebagai sistem utama untuk proses keuangan yang membutuhkan akurasi, konsistensi, dan dokumentasi resmi. Biasanya software ini digunakan untuk:</p><ul><li><strong>Menyusun laporan keuangan otomatis</strong>, seperti laba rugi, neraca, perubahan ekuitas, arus kas, atau CALK.</li><li><strong>Mengelola invoice secara sistematis</strong>, sehingga status tagihan masuk dan keluar bisa dipantau dengan lebih jelas.</li><li><strong>Mengurangi human error</strong>, karena sistem biasanya memiliki struktur akun, validasi data, dan alur input yang lebih ketat.</li></ul><p>Kalau Google Sheets unggul dalam fleksibilitas, maka software akuntansi unggul dalam standardisasi.</p><p><strong>Tantangan di lapangan</strong></p><p>Salah satu tantangan yang saya temui selama magang adalah bagaimana mengelola aktivitas operasional harian yang perubahannya cukup cepat.</p><p>Kalau setiap perubahan kecil langsung dimasukkan ke software akuntansi, proses kerja bisa jadi tidak efisien. Terlalu banyak revisi kecil justru berisiko membuat input di sistem utama menjadi kurang rapi.</p><p>Namun, kalau seluruh proses hanya bergantung pada Google Sheets, ada risiko lain yang tidak kalah besar: <strong>human error</strong>. Salah rumus, salah referensi data, atau salah nominal bisa berdampak pada hasil akhir.</p><p>Dari situ saya belajar bahwa kedua tools ini tidak seharusnya diposisikan sebagai pilihan salah satu. Justru yang lebih efektif adalah membagi peran keduanya dengan jelas.</p><p><strong>Solusi: hybrid workflow</strong></p><p>Pendekatan <strong>hybrid workflow</strong>, yaitu alur kerja yang menggabungkan fleksibilitas spreadsheet dan akurasi software akuntansi.</p><p>Secara sederhana, alurnya bisa seperti ini:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/937/1*V-j6_bEe0I7CZ2CnEWSQLw.png" /><figcaption>Hybrid workflow</figcaption></figure><ol><li><strong>Pencatatan harian di Google Sheets</strong></li></ol><p>Aktivitas operasional yang masih dinamis dicatat terlebih dahulu di Google Sheets. Misalnya, tracker pembayaran, monitoring invoice, atau catatan transaksi yang masih perlu dicek ulang.</p><p><strong>2. Review berkala</strong></p><p>Data yang sudah terkumpul kemudian ditinjau kembali secara berkala, misalnya mingguan atau bulanan. Pada tahap ini, data dirapikan, dicocokkan, dan divalidasi bersama pihak terkait.</p><p><strong>3. Finalisasi di software akuntansi</strong></p><p>Setelah data dipastikan bersih dan valid, barulah diinput ke software akuntansi sebagai pencatatan resmi perusahaan.</p><p>Dengan alur seperti ini, Google Sheets berfungsi sebagai ruang kerja yang fleksibel, sedangkan software akuntansi tetap menjadi <em>single source of truth</em> untuk laporan keuangan resmi.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f41af245850b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/google-sheets-vs-software-akuntansi-di-tim-finance-keduanya-justru-saling-melengkapi-f41af245850b">Google Sheets vs Software Akuntansi? Di Tim Finance, Keduanya Justru Saling Melengkapi</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agile vs Waterfall di Project Nyata: Teori Keren, Tapi Realitanya Nggak Sesimpel Itu]]></title>
            <link>https://medium.com/dot-intern/agile-vs-waterfall-di-project-nyata-teori-keren-tapi-realitanya-nggak-sesimpel-itu-9f6056436145?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/9f6056436145</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[agile-vs-waterfall]]></category>
            <category><![CDATA[project-management]]></category>
            <category><![CDATA[waterfall-development]]></category>
            <category><![CDATA[agile-development]]></category>
            <dc:creator><![CDATA[seradevi]]></dc:creator>
            <pubDate>Thu, 23 Jul 2026 15:14:44 GMT</pubDate>
            <atom:updated>2026-07-23T15:14:42.860Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a6M5YCpqjs8yOdl5nq4cIQ.png" /></figure><p>Kalau kamu pernah belajar soal software development, pasti udah nggak asing sama dua <em>kubu besar</em> ini: <em>Agile vs Waterfall</em>.</p><p>Di teori, keduanya kelihatan jelas banget bedanya.</p><p><em>Waterfall </em>itu <em>terstruktur, linear, dan rapi</em>.<br><em>Agile </em>itu <em>fleksibel, iteratif, dan adaptif</em>.</p><p>Tapi… pas masuk dunia kerja, ternyata nggak sesimpel itu.</p><p>Berdasarkan pengalaman saya sebagai <em>Project Manager Intern</em> di DOT, realitanya justru lebih abu-abu. Ingat sekali waktu <em>upgrading </em>materi <em>project management</em>, pemateri (salah satu PM di DOT) bertanya ke kami-kami <em>project manager intern </em>begini,</p><p><em>“Menurut kalian, project-project yang kalian pegang ini pakai metode apa? Waterfall atau Agile?”</em></p><p><em>Surprisingly</em>, hampir dari kami semua kegocek😅 Banyak dari teman-teman salah kira <em>project</em>nya menggunakan <em>agile</em>. Hanya 1 orang yang benar karena <em>project </em>yang ia pegang beneran <em>agile </em>menggunakan dokumen berita acara <em>sprint-based</em>. Padahal sehari-harinya kami melakukan koordinasi dengan tim <em>dev </em>layaknya metode <em>agile</em>. Nyatanya, selain satu yang benar itu, semua <em>project </em>menggunakan metode <em>waterfall </em>dengan <em>scope</em>, <em>budget</em>, dan <em>timeline </em>yang jelas.</p><p>Nah loh, kami semua be like: “Haaaaaah? Kok Bisaaaa?” 😳</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*zxHYHzGY1_5sNB6RtLNvXQ.gif" /></figure><h4>Agile vs Waterfall: Versi Singkat</h4><p><strong>Waterfall</strong> itu cocok kalau:</p><ul><li><em>Scope </em>udah jelas dari awal</li><li>Semua <em>requirement fix</em></li><li>Perubahan minim</li></ul><p>Royce (1970) menjelaskan bahwa model <em>Waterfall </em>berjalan secara berurutan dari tahap <em>requirement </em>sampai <em>maintenance</em>, dengan asumsi bahwa semuanya sudah direncanakan di awal.</p><p>Makanya, model ini sering dipakai di <em>project </em>yang berbasis kontrak karena semuanya harus jelas di depan.</p><p><strong>Agile</strong>, di sisi lain, lebih fleksibel.</p><ul><li><em>Requirement </em>bisa berubah</li><li><em>Development </em>jalan per iterasi (<em>sprint</em>)</li><li><em>Feedback </em>jadi bagian dari proses</li></ul><p>Di <em>Agile Manifesto</em>, bahkan disebutkan:</p><blockquote>Responding to change over following a plan<em> (Beck et al., 2001)</em></blockquote><p>Alias: perubahan itu bukan masalah, tapi justru bagian dari proses.</p><p>Secara teori, dua metode ini kayak berlawanan arah. Saling bertolak belakang. Seperti tidak mungkin saja akan dipakai secara bersamaan.</p><p>Tapi di dunia kerja?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*c4YE14x1iGiUcToc_EO72g.gif" /></figure><h4>Realita di Dunia Kerja, Hybrid is the Real Winner</h4><p>Di <em>project </em>berbasis <em>client</em>, biasanya kita mulai dengan pendekatan <em>Waterfall</em>:</p><ul><li><em>Scope </em>ditentukan di awal</li><li><em>Timeline </em>disepakati</li><li><em>Deliverables </em>jelas</li></ul><p>Tapi masalahnya… <em>client </em>hampir pasti akan kasih <em>feedback </em>di tengah jalan. <em>Feedback </em>itu bisa berupa; revisi kecil, <em>improvement</em>, bahkan <em>request </em>baru.</p><p>Di titik ini, kita “dipaksa” buat jadi <em>Agile</em>.</p><p>Boehm &amp; Turner (2004) juga menyebutkan bahwa banyak tim akhirnya pakai pendekatan <em>hybrid </em>karena butuh keseimbangan antara struktur (<em>Waterfall</em>) dan fleksibilitas (<em>Agile</em>).</p><p>Jadi ya… realitanya seperti ini,<br>👉 kita <em>planning </em>kayak <em>Waterfall,</em><br>👉 tapi <em>survive</em>-nya pakai <em>Agile.</em></p><p>Dan di sinilah peran <em>Project Manager</em> sebagai ujung tombak proyek menjadi penting. pake. banget.</p><h3>Studi Kasus: Ketika Feedback Client Datang di Waktu yang… Nggak Ideal</h3><p>Biar nggak terlalu teoritis, saya mau <em>share </em>satu studi kasus dari pengalaman saya.</p><p>Singkatnya di satu fase <em>project</em>, tim <em>dev </em>lagi jalan sesuai <em>timeline </em>dan <em>scope </em>yang sudah disepakati. <em>Scope </em>udah clear, <em>development </em>juga lagi <em>on track</em>. Tim sudah optimis akan sesuai ekspektasi awal.</p><p>Terus… masuklah sesi <em>review </em>dengan <em>client</em>.<br>Seperti yang bisa ditebak dan yap, ada <em>feedback^^</em>. Beberapa di antaranya:</p><ul><li>Optimasi performa website</li><li>Penyesuaian tampilan di fitur tertentu</li><li>Adjustment parameter suatu fitur</li></ul><p>Sebagian kelihatan simple.<br>Sebagian lagi… berpotensi “ngebongkar dapur”.</p><h4>Problemnya di Mana?</h4><p>Ini dia yang menarik.</p><p>Secara w<em>aterfall mindset, scope </em>sudah <em>fix </em>dan <em>timeline </em>harus dijaga. Namun secara <em>agile reality, client </em>butuh <em>improvement </em>dan <em>feedback </em>yang masuk nggak bisa dibiarin aja. Selain itu juga belum ada estimasi <em>effort, </em>belum tahu <em>impact </em>ke sistem, dan <em>resource </em>tim terbatas.</p><p>Kalau semua langsung dikerjain?</p><p>👉 <em>Timeline </em>bisa berantakan<br>👉 Tim bisa <em>overload</em></p><p><em>Not a good idea.</em></p><h4>Jadi, Apa yang Dilakukan?</h4><p>Di sini saya belajar satu hal penting sebagai <em>project manager</em>. <strong>Jangan reaktif. Harus tetap strategis. </strong>Langkah-langkah yang bisa diambil pada waktu itu adalah:</p><ol><li><strong>Breakdown Dulu, Jangan Panik. </strong>Semua feedback dipecah jadi lebih detail. <em>Sebenarnya yang mau diubah apa? Seberapa besar impact-nya? </em>Karena “<em>adjust </em>dikit” dari <em>client</em>, belum tentu kecil di sisi teknis 😄</li><li><strong>Ngobrol Sama Developer</strong> (hukumnya wajib). <em>Project Manager</em> nggak bisa ambil keputusan sendirian, harus melibatkan <em>stakeholder </em>yang terdampak, terutama pada saat <em>feedback </em>seperti ini tim <em>dev </em>merupakan <em>stakeholder </em>penting sebagai eksekutor. Diskusi dengan tim <em>dev </em>tujuannya untuk cek kompleksitas <em>feedback, </em>identifikasi risiko, dan melihat apakah ada kemungkinan <em>side effect</em> yang terjadi.</li><li><strong>Estimasi Sebelum Eksekusi</strong>. Ini krusial banget. PMBOK Guide (PMI, 2017) juga menekankan bahwa estimasi itu penting untuk kontrol waktu dan <em>resource</em>. Tanpa estimasi = <em>blind decision</em>.</li><li><strong>Prioritization </strong>alias nggak semua harus sekarang. Bisa kita pilah dengan memisahkan mana yang <em>urgent, </em>mana yang <em>impactful, </em>dan mana yang bisa ditunda.</li><li><strong>Alignment ke Stakeholder</strong>. Hasil dari penerapan tahap-tahap di atas ini nggak cuma disimpan sendiri aja dong. Semua harus dikomunikasikan baik ke <em>client</em> maupun ke <em>management </em>serta tim internal, mulai dari apa yang bisa dikerjakan sekarang, apa yang masuk <em>next phase</em>, atau apa yang jadi <em>additional scope</em>.</li></ol><h3>Lesson Learned</h3><p>Dari sini saya sadar beberapa hal;</p><p><strong>Project </strong>itu bisa nggak 100% <em>Agile </em>atau <em>Waterfall</em>. Bisa jadi ada campuran. Entah kontrak dan <em>deliverable </em>kepada <em>client </em>secara <em>waterfall</em>, lalu eksekusi dengan tim <em>dev </em>secara <em>agile</em>, atau <em>vice versa</em>.</p><p><strong>Project Manager </strong>itu bukan tukang <em>forward</em>. Bukan cuma nerusin <em>request </em>dari <em>client </em>ke <em>dev</em>. Tapi juga melakukan filter, analisis, dan sebagai <em>decision maker. </em>Kalau <em>project manager</em>nya saja tidak paham konteks, bisa jadi <em>project</em>nya akan berjalan di tempat.</p><p><strong>Estimasi </strong>= Senjata Utama. Tanpa estimasi, semua keputusan cuma <em>feeling</em>. No☝️ No☝️ ya☝️ Usahakan keputusan-keputusan yang akan diambil ini memiliki data yang jelas.</p><p><em>Agile vs Waterfall </em>itu memang seru buat dibahas dalam satu forum. Namun di dunia kerja, yang lebih penting bagi <em>Project Manager, </em>balik lagi ke fungsi awalnya, adalah <em>balancing </em>3 hal penting ini;<em><br>👉 Scope<br>👉 Time<br>👉 Cost</em></p><p>Karena pada akhirnya, project yang sukses bukan soal metode <em>development </em>yang “paling <em>Agile</em>” atau “paling <em>Waterfall</em>”. Tapi yang <strong>paling bisa menghadapi perubahan tanpa kehilangan arah project.</strong></p><p><em>Trust me</em>, di situlah peran <em>Project Manager</em> benar-benar diuji.</p><h3>Daftar Pustaka</h3><p>Beck, K., et al. (2001). <em>Manifesto for Agile Software Development</em>.</p><p>Boehm, B., &amp; Turner, R. (2004). <em>Balancing Agility and Discipline: A Guide for the Perplexed</em>. Addison-Wesley.</p><p>Project Management Institute. (2017). <em>A Guide to the Project Management Body of Knowledge (PMBOK® Guide)</em> (6th ed.).</p><p>Pressman, R. S. (2010). <em>Software Engineering: A Practitioner’s Approach</em> (7th ed.). McGraw-Hill.</p><p>Royce, W. W. (1970). <em>Managing the Development of Large Software Systems</em>. Proceedings of IEEE WESCON.</p><p>Schwaber, K., &amp; Sutherland, J. (2020). <em>The Scrum Guide</em>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9f6056436145" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/agile-vs-waterfall-di-project-nyata-teori-keren-tapi-realitanya-nggak-sesimpel-itu-9f6056436145">Agile vs Waterfall di Project Nyata: Teori Keren, Tapi Realitanya Nggak Sesimpel Itu</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Enhancing Minimalist Corporate Design with Glow Effect for Instagram Feed]]></title>
            <link>https://medium.com/dot-intern/enhancing-minimalist-corporate-design-with-glow-effect-for-instagram-feed-e5ed1cafe01f?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/e5ed1cafe01f</guid>
            <category><![CDATA[graphic-design]]></category>
            <category><![CDATA[glow-effect]]></category>
            <dc:creator><![CDATA[Geraldiyoas]]></dc:creator>
            <pubDate>Thu, 23 Jul 2026 04:01:02 GMT</pubDate>
            <atom:updated>2026-07-23T04:01:02.194Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CDzDc7Pq8XPa4VVf5BVbhw.jpeg" /><figcaption>Photo By <a href="https://unsplash.com/id/@sam_roy7093?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Sam Roy</a> On <a href="https://unsplash.com/id/foto/pola-cahaya-biru-dan-putih-bersinar-abstrak-XgcWdYnxZA0?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>In today’s digital age, an Instagram feed is often the first impression potential clients and business partners see, especially for technology and digital companies such as IT agencies.</p><p>A minimalist corporate design conveys a professional image and is easy to maintain consistently across various media, but it runs the risk of appearing flat and failing to catch people’s attention as they scroll, while a design that’s too busy can actually damage the brand’s professional image.</p><p>This is where the glow effect plays a key role, using subtle lighting techniques that add depth and visual appeal without compromising the minimalist aesthetic, ensuring the feed remains engaging while fostering a positive perception among the audience.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IABCJH8nZhCY72WecBStFA.jpeg" /><figcaption>Photo By <a href="https://unsplash.com/id/@daniyarorazov?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Daniyar Orazov</a> On <a href="https://unsplash.com/id/foto/orang-merekam-konser-dengan-smartphone-lampu-biru-FILOyRPTtPY?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><h3>What Is the Glow Effect, and Why Is It Suitable for Corporate Design?</h3><blockquote>According to Subframe, a well-executed glow effect is one where the chosen color complements the design and enhances the visibility of elements without looking excessive. Simply put, a glow effect is a lighting technique that makes certain elements such as icons, typography, or key visuals appear to emit a soft glow, thereby drawing attention to important parts without making the composition look cluttered.</blockquote><p>Rather than adding new elements, the glow effect highlights existing elements to make them more recognizable, creating the perfect balance for a look that remains clean and structured yet feels more modern in the feed.</p><h3>How Do You Apply a Glow Effect to a Minimalist Corporate Feed?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8RvFaO6__MpE_xToETr2VQ.jpeg" /><figcaption>Photo By <a href="https://unsplash.com/id/@fazurrehman?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Faizur Rehman</a> On <a href="https://unsplash.com/id/foto/pria-berbaju-hitam-duduk-di-depan-komputer-pHPzdEHN6Os?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>Here are a few ways to apply a glow effect to minimalist corporate designs.</p><ul><li><strong>Determine the Focal Point<br></strong>Before adding a glow, first identify one element you want to highlight such as a logo, product icon, or main headline. Applying a glow to too many elements at once will actually weaken the effect and undermine the principle of minimalism.</li><li><strong>Choose a Glow Color That’s Consistent with Your Brand<br></strong>Glow colors should be selected from the brand’s existing color palette typically primary or accent colors. Choose colors that truly support and enhance readability, rather than colors that are so intense they strain the eyes. This ensures the effect blends seamlessly with the overall visual identity, rather than being merely a generic, afterthought embellishment.</li><li><strong>Apply Layered Blur and Opacity to Add Depth<br></strong>In Figma, this is done by duplicating the focal point element, then applying a layer blur or background blur with a gradually increasing radius, and reducing the opacity of each layer. Using the right amount of transparency makes the glow appear soft and adds depth without looking too harsh or distracting. This layered approach produces a soft gradation of light, rather than a flat, artificial-looking glow.</li><li><strong>Intensity and Placement Control</strong><br>The glow should be strong enough to be noticeable, but still subtle enough not to overpower the minimalist elements around it. Placing the glow slightly off-center or behind the typography often creates a more natural and premium look.</li><li><strong>Consistency Check Across the Grid Feed<br></strong>A single post isn’t enough to judge the final result. The glow effect needs to be tested alongside other posts on the Instagram grid to ensure visual consistency and to keep the overall feed looking cohesive and professional.</li></ul><h3>Case Study: Applying the Glow Effect to DOT Indonesia Conten</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wg6Yi_YPm9Y7K5pI8AVNvw.png" /><figcaption>Design By DOT Indonesia</figcaption></figure><p>As a concrete example, one of DOT Indonesia’s educational slides is titled:</p><blockquote><em>“Don’t Put Business Logic in the Scheduler”</em></blockquote><p>which demonstrates how the glow effect can be effectively applied to minimalist corporate design. Here are a few things we can learn from this design:</p><ul><li><strong>A clear focal point<br></strong>The glowing green circle on the left immediately catches the eye and serves as the starting point of the diagram’s flow, connecting the Scheduler, Job, Worker, and Service elements.</li><li><strong>Warna glow konsisten dengan brand<br></strong>The bright green color used for the glow effect matches the DOT brand’s accent color, so it blends seamlessly with the logo and visual identity and not just a decorative addition.</li><li><strong>Contrast against the dark background<br></strong>The minimalist dark green background allows the glow to really stand out without compromising the readability of the title, subtitle, or chart labels.</li><li><strong>Luminous line and dot elements<br></strong>The lines connecting the elements are also given a subtle glow effect, so that the flow of information remains dynamic without being excessive.</li><li><strong>The layout remains structured<br></strong>Although there are striking luminous elements, the grid layout, typography, and supporting elements such as photos of interviewees and page numbers (01/07) remain neat and consistent with DOT Indonesia’s minimalist style.</li></ul><p>This case study shows that the glow effect doesn’t have to be applied to designs that are bright or colorful. In fact, against a dark, minimalist background, the glow effect can serve as a strong distinguishing element while still maintaining a professional and easy-to-read look.</p><blockquote><strong>Why Is This Technique Important for Corporate Branding?</strong></blockquote><p>The right use of the glow effect helps a minimalist corporate feed stand out amid a crowded Instagram timeline, without compromising the professional image expected by a corporate audience. This demonstrates that a company pays attention to detail and keeps up with design trends, which can indirectly strengthen the trust of clients and business partners.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*y2fJWOfW6uK-7Yj2wn7CGA.jpeg" /><figcaption>Photo By <a href="https://unsplash.com/id/@anniespratt?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Annie Spratt</a> On <a href="https://unsplash.com/id/foto/sekelompok-orang-yang-menggunakan-komputer-laptop-QckxruozjRg?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><h3>Conclusion</h3><p>Combining a glow effect with a minimalist corporate design allows a brand to remain visually appealing without losing the clean, professional identity that builds audience trust. By defining a clear focal point, staying consistent with the brand’s color palette, applying layered blur and opacity to make the glow look natural, and reviewing the results across the entire feed, designers can enhance the quality of a corporate Instagram presence while maintaining brand consistency, as seen in its application in one of DOT Indonesia’s educational posts.</p><p>References :</p><p>Subframe. <a href="https://www.subframe.com/tips/css-glow-effect-examples">10 CSS Glow Effect Examples</a></p><p>Adobe. <a href="https://www.adobe.com/learn/photoshop/paths/design/photoshop/series/graphic-design/create-design-effects/photoshop/in-app/create-a-glow-effect">Create a Glow Effect</a></p><p>Onsight B2B Sales Blog. <a href="https://www.onsightapp.com/blog/minimalism-in-branding-is-less-more">Minimalism in Branding: Is Less More?</a></p><p>Edge Digital. <a href="https://www.edgedigital.net/the-guide-to-clean-minimal-sleek-branding/">Clean Minimal Branding: Key Strategies for Effective Visual Identity</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e5ed1cafe01f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/enhancing-minimalist-corporate-design-with-glow-effect-for-instagram-feed-e5ed1cafe01f">Enhancing Minimalist Corporate Design with Glow Effect for Instagram Feed</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Optimisasi Re-Render Pada Project React]]></title>
            <link>https://medium.com/dot-intern/optimisasi-re-render-pada-project-react-95f5299ed393?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/95f5299ed393</guid>
            <category><![CDATA[usememo]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[suspense]]></category>
            <category><![CDATA[react-compiler]]></category>
            <category><![CDATA[useref]]></category>
            <dc:creator><![CDATA[lleans]]></dc:creator>
            <pubDate>Fri, 09 Jan 2026 00:43:11 GMT</pubDate>
            <atom:updated>2026-01-09T00:43:10.905Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*y6iXaTF8BlCwSfGvolF4mQ.jpeg" /></figure><p>Pernah nggak kalian, kenapa button submit serasa lambat pada saat ditekan, padahal hanya merubah satu state?</p><p>Yap, mungkin itu jadi alasan kalian sampai ke artikel ini. Ini disebut dengan uncontrolled re-render, yang kalian rasakan adalah hasil dari re-render yang tidak terkontrol dengan baik.</p><blockquote>Kenapa re-render buruk?</blockquote><p>Bayangin aja, kalian hanya bikin aplikasi simpel, tapi serasa lambatnya minta ampun. UI tidak responsive dan masih banyak lagi. Ini terjadi karena pada perubahan state di component, react akan me-render semua child component tersebut, jika tidak di handle dengan baik. Maka dari itu pada kesempatan ini, aku akan berbagi tips soal mengatasi re-render di react.</p><p>Tapi sebelum itu, kita pahami dulu gimana history react secara dasar melakukan render?</p><h3><em>React history</em></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*fA76ccLG6J82OxEBtpAP8g.gif" /></figure><p>Pada tahun 2011 membuat website adalah hal yang <strong>mudah membuat frustrasi</strong>, walaupun pada saat itu sudah ada beberapa framework yang membantu dalam web development. diantaranya jQuery-Backbone.js dan Angular.js. Framework tersebut adalah inovasi terbesar pada zaman itu.</p><p>Namun, seiring aplikasi membesar, struktur kode menjadi berantakan dan sulit melacak <em>bug</em> — fenomena ini dikenal sebagai “<em>jQuery fatigue</em>”.</p><blockquote><em>DOM, Document Object Model. adalah struktur yang ada pada html, semua tag pada html adalah bentuk kecil pada DOM, &lt;html&gt;-&lt;body&gt;-&lt;p&gt;-&lt;div&gt;</em></blockquote><p>Kemudian, <strong>Backbone.js</strong> hadir dengan struktur yang lebih rapi, memisahkan data (<em>state</em>) dan tampilan (DOM). Sayangnya, fiturnya terlalu minimalis, sehingga developer harus membangun banyak hal dari nol dan perawatannya pun jadi susah.</p><p>Lalu munculah AngularJS dengan fitur melimpah. Masalahnya, performanya jadi lambat di aplikasi besar. Angular terus-menerus memeriksa perubahan di seluruh halaman (dirty checking), yang membuat interaksi sederhana seperti klik tombol pun bisa terasa lemot.</p><blockquote>Oke, jadi react dibuat untuk mengatasi masalah AngularJS?</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*_46YK7WYHyc0fYKryRfaSQ.gif" /></figure><p>Yap, betul. Dibuat oleh Engineer Facebook(Meta), mereka sebagai platform besar menghadapi masalah ini, yang mana mengupdate UI menjadi momok yang menakutkan.</p><p>Jordan Walke, salah satu engineer facebook. dia punya ide untuk mengatasi masalah AngularJS ini, yaitu:</p><blockquote>Bagaimana kalau daripada mengupdate bagian spesifik pada DOM. kenapa tidak di render ulang semua.</blockquote><p>Ya, kalian gak salah baca, dia malah memberikan ide yang berlawanan dengan inovasi AngularJS. Ini karena, me-re render semua bukanlah hal efisien, pada framework sebelumnya sudah ada inovasi spesifik rendering agar lebih mudah dan efisien, malah kembali ke zaman batu. Bahkan engineer lainya juga tidak setuju dengan ide tersebut.</p><p>Tapi dia punya ide lagi untuk mengatasi masalah ini. munculah <strong>Virtual DOM</strong></p><h3>Virtual DOM</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6dDw4eI98H2QfZY4NGf4rQ.png" /><figcaption><a href="https://adhithiravi.medium.com/react-virtual-dom-explained-in-simple-english-fc2d0b277bc5">https://adhithiravi.medium.com/react-virtual-dom-explained-in-simple-english-fc2d0b277bc5</a></figcaption></figure><p>Anggap saja sebagai salinan ringan dari real DOM. Secara simpelnya begini, setiap state aplikasi berubah react akan membandingkan dengan Virtual DOM versi lama dengan yang baru. Dilihat lah nanti perbandingannya dari paling yang kecil, jika sudah, nanti di aplikasikan ke real DOM.</p><blockquote>Hah, masih gak paham?</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*yhXbRA2fFJ8TM7iU0ZSwDQ.gif" /></figure><p>Oke, Bayangkan kita perlu mengedit <strong>halaman 100</strong> dari sebuah buku setebal 300 halaman.</p><p><strong>Cara Angular (Dirty Checking):</strong></p><ul><li>Membaca ulang <strong>seluruh buku asli</strong> dari halaman pertama untuk mencari apa yang berubah.</li><li>Setelah ketemu di halaman 100, langsung mengubahnya di buku asli.</li></ul><p><strong>Cara React (Virtual DOM):</strong></p><ul><li>Membuat <strong>salinan virtual</strong>(semacam daftar isi super detail) dari buku.</li><li>Saat ada editan, React mnegedit pada salinannya lalu <strong>membandingkan keduanya</strong> untuk menemukan perbedaannya (yaitu, perubahan di halaman 100).</li><li>React kemudian langsung membuka <strong>halaman 100 di buku asli</strong> dan menerapkan perubahan spesifik itu.</li></ul><blockquote>Kayaknya extra steps, bukannya malah cepetan angular?</blockquote><p>Sekilas begitu, tapi secara <strong>real-practice</strong>, melakukan scanning pada virtual copy lebih cepat dan efisien. Terlebih lagi, kita bisa langsung mengedit pada halaman spesifik real dom, tidak perlu melakukan scanning awal pada real dom.</p><h3>Re-rendering Issue</h3><blockquote>Okay, begitu, kenapa kita perlu merhatikan re-rendering, kan udah ada vitual dom buat ngurus itu.</blockquote><p>Nah, aku jelaskan dulu. Kita semua paham kalau react menggunakan component based. Jadi bagian-bagian dari react berisi dengan komponen-komponen yang bisa membangun sebuah aplikasi yang bisa digunakan.</p><p>Nah, komponen ini adalah yang dimaksud dengan bagian kecil dari virtual dom.</p><blockquote>Hah gimana?</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*tAIY-jga3U7MtgkgOOiSvQ.gif" /></figure><p>Oke aku jelaskan, alasan react bisa menjadi cepat dan lebih unggul dari angular salah satunya karena ini, jadi jika ada changes, dia akan mengupdate bagian komponen itu sendiri. Jadi react nanti hanya perlu mengecek pada bagian komponen tersebut. tidak perlu pada semua dom.</p><p>Dengan begitu, react bisa menjadi lebih powerful daripada angular.</p><blockquote>Oke, kalau begitu kenapa kita perlu memerhatikan re render ini?</blockquote><p>Oke, aku sudah jelaskan kalau komponen adalah bentuk paling kecil pada virtual dom. Nah kalau komponen itu update maka dia akan <strong>me-render ulang pada semua komponen itu di virtual dom</strong>. ya memang kelihatan sepele, orang hanya di virtual dom yang ringan. tapi semakin besar aplikasi kita, maka peforma itu akan semakin terasa.</p><p>Untuk menghindari itu, kita harus biasakan dalam menulis kode yang baik, kebiasaan ini dimulai dari hal-hal kecil yang harus disiplin kita lakukan. Berikut beberapa saran untuk mengoptimalkan re-render di aplikasi React Kamu.</p><h4>1. Simpan data Mutable di useRef daripada useState</h4><blockquote>Hanya berlaku pada variabel yang belum di render/tidak ditampilkan ke user</blockquote><p>Sebagai developer React, kita sering kebiasaan menggunakan useState dan melupakan useRef. Padahal, fungsi keduanya sangat berbeda.</p><ul><li><strong>useState</strong> digunakan untuk menyimpan data yang ketika berubah, perlu re-render pada komponen. Contoh, jika kita ingin mengubah counter di tampilan halaman(component yang <strong>sudah di render</strong>), maka dengan hook ini, React akan mentrigger render ulang.</li><li><strong>useRef</strong> tidak mentrigger re-render saat nilainya diubah. Dengan useRef, kita bisa menyimpan dan mengubah variabel tanpa perlu melakukan re-render komponen.</li></ul><pre>// ❌ Tidak optimal<br>const FormComponent = () =&gt; {<br>  const [formData, setFormData] = useState({<br>    name: &#39;&#39;,<br>    email: &#39;&#39;,<br>    // variabel validasi yang tidak perlu ditampilkan<br>    isValid: false <br>  });<br>  <br>  // saat validasi di panggil, komponen akan di re-render<br>  const validateForm = () =&gt; {<br>    setFormData(prev =&gt; ({<br>      ...prev,<br>      isValid: prev.name.length &gt; 0 &amp;&amp; prev.email.includes(&#39;@&#39;)<br>    }));<br>  };<br>};</pre><pre>// ✅ Optimal<br>const OptimizedFormComponent = () =&gt; {<br>  const [formInputs, setFormInputs] = useState({<br>    name: &#39;&#39;,<br>    email: &#39;&#39;<br>  });<br>  <br>  // Gunakan useRef untuk data yang tidak perlu me-render UI<br>  const formStateRef = useRef({<br>    isValid: false,<br>    lastUpdated: null<br>  });<br>  <br>  // saat validasi di panggil, komponen tidak akan di re-render<br>  const validateForm = () =&gt; {<br>    formStateRef.current.isValid = <br>      formInputs.name.length &gt; 0 &amp;&amp; formInputs.email.includes(&#39;@&#39;);<br>    formStateRef.current.lastUpdated = new Date();<br>  };<br>};</pre><p>Mungkin hal ini sepele, tapi biasanya kita lupa dengan hal ini, seiringnya dengan adanya state-management. kadang kita terpaku dengan menggunakan itu, yang mana case-nya sama-sama mentrigger re-render. useRef memang hal sepele, tapi bisa kita gunakan dengan baik dengan mengetahui fungsinya yang telah di jelaskan di atas.</p><p>berikut contoh sebelum dan sesudah</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f4GtL7p8bMa1SKcoymX6Nw.gif" /></figure><h4>2. Gunakan useMemo, tapi dengan hati-hati!!</h4><blockquote>💡Terlalu banyak memoization juga buruk untuk performa</blockquote><p>Oke, ini hal tricky. Memoization memang penyelamat, tapi semakin banyak penggunaannya, maka semakin buruk. Jadi kita harus berhati-hati kapan menggunakan useMemo dan kapan tidak.</p><p>useMemo, memudahkan kita dalam mengkontrol re render ini. Dengan useMemo, kita bisa menyuruh sebuah function atau komponen untuk melakukan update ke memori dengan ketentuan spesifik, jadi tidak selalu di lakukan re-render setiap state change.</p><p>Kita ambil contoh, anggap kita perlu membuat component yang statis, contoh saja disini membuat opsi sleect yang bersisi data constant.</p><pre>const MainKompt = () =&gt; {<br> const [state, setState] = useState()<br> <br> const StaticKomp = () =&gt; {<br>  const heavyDataPros = () =&gt; {<br>   const options = [&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;H&quot;, &quot;I&quot;]<br>   new Promise(() =&gt; setTimeout(3000))<br>   return options.map((it) =&gt; {<br>    label: it,<br>    value: it<br>   })<br>  }<br>  <br>  return &lt;Select placeholder=&quot;Select options&quot; options={heavyDataProps} /&gt;<br> }<br> <br> return &lt;&gt;<br>   &lt;button onClick={() =&gt; setState((prev) =&gt; prev++)}&gt;increment&lt;/button&gt;<br>   &lt;h1&gt;{state}&lt;/h1&gt;<br>   &lt;StaticKomp /&gt;<br>  &lt;/&gt;<br>}</pre><p>Dari sini kita bisa dilihat, kalau StaticKomp memiliki data statis yang akan sama terus menerus, tapi membutuhkan perhitungan lama. Ketika react melakukan render ulang pada MainKompt, component tersebut akan merender ulang child trsebut, meskipun tidak ada hubungan state.</p><p>Daripada melakukan re-render pada child, kita bisa membuat child untuk menggunakan useMemo. Jadi kita cukup mengingat component ini, tanpa perlu melakuakn re-render berkali-kali.</p><pre>const MainKompt = () =&gt; {<br> const [state, setState] = useState()<br> <br> const StaticKomp = useMemo(() =&gt; {<br>  const heavyDataPros = () =&gt; {<br>   const options = [&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;H&quot;, &quot;I&quot;]<br>   new Promise(() =&gt; setTimeout(3000))<br>   return options.map((it) =&gt; {<br>    label: it,<br>    value: it<br>   })<br>  }, [])<br>  <br>  return &lt;Select placeholder=&quot;Select options&quot; options={heavyDataProps} /&gt;<br> }<br> <br> return &lt;&gt;<br>   &lt;button onClick={() =&gt; setState((prev) =&gt; prev++)}&gt;increment&lt;/button&gt;<br>   &lt;h1&gt;{state}&lt;/h1&gt;<br>   &lt;StaticKomp /&gt;<br>  &lt;/&gt;<br>}</pre><p>dengan ini, maka useMemo akan mengingat StaticKompt, yang mana jika sudah di render saat pertama kali load. Dengan ini, ketika state update pada MainKompt, load akan lebih cepat, karena StaticKompt tidak perlu di render ulang.</p><p>Ini contoh sebelum dan sesudahnya.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NUEbqJNwR3KR78DhOamABA.gif" /></figure><h4>3. Gunakan component &lt;Suspense/&gt;</h4><blockquote>Hanya berlaku pada penggunaan framework yang support SSR(Server Side Rendering)</blockquote><p>Kadang dalam menghandle fetching data, kita perlu menampilkan loading state agar user dapat mengetahui kalau aplikasi sedang melakukan fetching data.</p><p>Sering kita menggunakan conditional operator &amp;&amp; untuk mengatasi ini, tapi itu bukanlah hal efektif karena beberapa hal yang diperhatikan:</p><ul><li>Kita perlu menulis berkali-kali kondisi ini untuk menampilkan loading state. Sedangan dengan suspense, kita bisa membuat custom component untuk loading dan menggunakannya dimana saja.</li><li>Dengan suspense kita bisa melakukan partial loading pada halaman tersebut, yang bisa melakukan improvisasi pada <a href="https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#what-is-streaming">Performance Metrics</a></li><li>Kode lebih enak di baca dengan adanya suspense</li></ul><p>Berikut contoh yang bisa diambil di praktekkan dengan React biasa:</p><pre>import { use } from &quot;react&quot;;<br><br>const fetchUser = async () =&gt; {<br>  // artificial 2 second delay<br>  await new Promise((resolve) =&gt; setTimeout(resolve, 2000));<br><br>  const response = await fetch(&quot;https://meowfacts.herokuapp.com/&quot;);<br>  if (!response.ok) {<br>    throw new Error(&quot;Failed to fetch user&quot;);<br>  }<br><br>  const { data } = await response.json();<br>  return data;<br>};<br><br>const promise = fetchUser();<br><br>const MeowPage = () =&gt; {<br>  const data = use(promise);<br>  return &lt;h1&gt;{data[0]}&lt;/h1&gt;;<br>};<br><br>export default MeowPage;</pre><pre>import { Suspense } from &quot;react&quot;;<br>import MeowPage from &quot;./meow&quot;;<br><br>const MeowClient = () =&gt; {<br>  return (<br>    &lt;Suspense fallback={&lt;p&gt;💭 Lagi loading….&lt;/p&gt;}&gt;<br>      &lt;MeowPage /&gt;<br>    &lt;/Suspense&gt;<br>  );<br>};<br><br>export default MeowClient;</pre><p>Berikut contoh sebelum dan sesudah</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1DTPIPQnILTv_eTLpNJhYw.gif" /></figure><p>Memang sekilas agak terlihat rumit, tapi jika usecase dalam sebuah project kita menggunakan framework sepertireact-query dan nextjs. mereka secara langsung mensupport keberadaan suspense ini, yang mana di react-query, cukup dengan menggunakan useSuspenseQuery dan ReactQueryStreamedHydration dari react query</p><p><a href="https://github.com/TanStack/query/tree/main/examples/react/nextjs-suspense-streaming">query/examples/react/nextjs-suspense-streaming at main · TanStack/query</a></p><pre>import { useSuspenseQuery } from &quot;@tanstack/react-query&quot;;<br><br>const fetchUser = async () =&gt; {<br>  // artificial 2 second delay<br>  await new Promise((resolve) =&gt; setTimeout(resolve, 2000));<br><br>  const response = await fetch(&quot;https://meowfacts.herokuapp.com/&quot;);<br>  if (!response.ok) {<br>    throw new Error(&quot;Failed to fetch user&quot;);<br>  }<br><br>  const { data } = await response.json();<br>  return data;<br>};<br><br>const MeowComponent = () =&gt; {<br>  const { data } = useSuspenseQuery({<br>    queryKey: [&quot;meong&quot;],<br>    queryFn: fetchUser,<br>  });<br>  return &lt;h1&gt;{data[0]}&lt;/h1&gt;;<br>};<br><br>export default MeowComponent;</pre><pre>import { Suspense } from &quot;react&quot;;<br>import MeowPage from &quot;./meow&quot;;<br>import {<br>  QueryClient,<br>  QueryClientProvider,<br>} from &#39;@tanstack/react-query&#39;<br>import { ReactQueryStreamedHydration } from &#39;@tanstack/react-query-next-experimental&#39;<br><br>const MeowClient = () =&gt; {<br> const queryClient = new QueryClient()<br><br>  return (<br>   &lt;QueryClientProvider client={queryClient}&gt;<br>      &lt;ReactQueryStreamedHydration&gt;<br>      &lt;Suspense fallback={&lt;p&gt;💭 Lagi loading….&lt;/p&gt;}&gt;<br>        &lt;MeowPage /&gt;<br>      &lt;/Suspense&gt;<br>     &lt;/ReactQueryStreamedHydration&gt;<br>   &lt;/QueryClientProvider&gt;<br>  );<br>};<br><br>export default MeowClient;</pre><p>Sedangkan pada nextjs, bisa kita manfaatkan fitur app router, yang mana cukup dengan membuat file bernama loading.tsx di <a href="https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming">direktori</a>, maka secara langsung saat menggunakan component Suspense, akan menggunakan file tersebut sebagai fallback.</p><h4>4. React Compiler</h4><blockquote>Minimum Support React 17</blockquote><p>Diperkenalkan pada 2021 di React Conf, saat itu masih dinamai React Forget. Tujuan dibuat ini, untuk melupaakan optimisasi react. Maksudnya kita cukup menjalankan react compiler maka useMemo dan optimisasi lainnya akan dilakukan secara otomatis dengan mudah.</p><p>Berikut contoh optimisasi-nya</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NyUBlFDLSBLjJaRHgIxh9w.gif" /></figure><p>Untuk dokumentasi, bisa kita lihat di halaman react berikut</p><p><a href="https://react.dev/learn/react-compiler">React Compiler - React</a></p><blockquote>Tau gini, langsung kasih tau dong dari awal!</blockquote><p>Ya memang sih, tapi dengan mengetahui semua itu. Kita bisa memahami bagaimana cara menulis kode di react dengan baik dan benar. Ingat semua dimulai dari kebiasaan kecil 🫡</p><p>Berikut untuk source code aplikasi yang mempraktekkan perbedaan sebelum dan sesudah</p><p><a href="https://github.com/lleans/render-optimize">GitHub - lleans/render-optimize</a></p><h3>Kesimpulan</h3><p>Sampailah kita ke kesimpulan. Disini kita bisa belajar kalau optimiasai react itu dimulai dengan dari kedisiplinan kita sendiri, abagimana kita cara menulisa kode, bagaimana kita menhandle variable dan komponen, dan masih banyak lagi. Selain dari cara penulisan kode, ada juga dari kebiasaan kita dalam menggunakan library.</p><p>Sedikit tips kecil, jika memang hal itu simpel atau mudah untuk di lakukan, cukup lakukan saja. Jangan malas menggunakan libary atau apa, selama itu masih bisa di selesaikan seacara dasar, maka lakukanlah. Oke segitu aja tips dari aku, happy coding ✨.</p><h3>Referensi</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FN3xBcEbbl80%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DN3xBcEbbl80&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FN3xBcEbbl80%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/9a71384147182ce8c88b7817535e1a9b/href">https://medium.com/media/9a71384147182ce8c88b7817535e1a9b/href</a></iframe><ul><li><a href="https://www.joshwcomeau.com/react/why-react-re-renders/?utm_source=chatgpt.com">Why React Re-Renders * Josh W. Comeau</a></li><li><a href="https://www.developerway.com/posts/react-re-renders-guide?utm_source=chatgpt.com">React re-renders guide: everything, all at once</a></li><li><a href="https://medium.com/@ztolley/react-re-render-optimisation-d16ba64754a5">React Re-render optimisation</a></li><li><a href="https://dev.to/trinityyi/when-to-use-useref-instead-of-usestate-3h4o">When to use useRef() instead of useState()</a></li><li><a href="https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming">File-system conventions: loading.js | Next.js</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=95f5299ed393" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/optimisasi-re-render-pada-project-react-95f5299ed393">Optimisasi Re-Render Pada Project React</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bikin Chatbot Pakai Mastra.AI Atau LangChain?]]></title>
            <link>https://medium.com/dot-intern/bikin-chatbot-pakai-mastra-ai-atau-langchain-db4f78422d46?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/db4f78422d46</guid>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[langchain]]></category>
            <category><![CDATA[mastra-ai]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[ai-agent]]></category>
            <dc:creator><![CDATA[Reza Arya Wijaya]]></dc:creator>
            <pubDate>Fri, 02 Jan 2026 04:36:01 GMT</pubDate>
            <atom:updated>2026-01-02T04:36:01.378Z</atom:updated>
            <content:encoded><![CDATA[<h4>Alergi Python? Ngoding Python banyak tingkah? Gasss pake TypeScript Aja!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/0*wIlTrDaPBGYZy4mp" /><figcaption>Sumber : <a href="https://www.linkedin.com/pulse/i-stopped-learning-python-focused-typescriptheres-how-pandharkar-dqszf/">https://www.linkedin.com/pulse/i-stopped-learning-python-focused-typescriptheres-how-pandharkar-dqszf/</a></figcaption></figure><p>Bikin chatbot berarti kamu harus berurusan dengan LLM, berurusan dengan LLM identik dengan berurusan dengan Python (setidaknya itu sih yang aku tau). Framework yang paling <em>famous</em> buat urusan LLM sudah pasti LangChain, dan LangChain adalah Python<em> </em>based framework.</p><blockquote>🗣️“T-tapi bang, aku alergi Python”</blockquote><p>Agak aneh sih mau ‘mainan’ LLM tapi alergi Python, tapi gapapa, LangChain juga ada kok buat JavaScript. N8N juga bisa jadi alternatif yang bagus kalau kalian gamau ngoding.</p><blockquote>🗣️“N8N??? Alergi bang, aku maunya ngoding”</blockquote><blockquote>️️️🗣️“JavaScript??? T-t-tapi bangg, aku sukanya TypeScript, trus LangChain masih bau Python, gasuka gelay~”</blockquote><p>ribet bener…yaudah sini aku kenalin sama Mastra AI.</p><p>Mastra AI adalah <strong>TypeScript based framework </strong>(yes, bahasa favorit kalian) yang bisa kalian manfaatkan untuk membuat Chat Agent berbasis AI atau mungkin aplikasi berbasis AI. Bedanya sama LangChain, Mastra AI <strong>lebih bersahabat dengan developer</strong> karena banyak fitur yang membantu banget selama proses development. Di Mastra, kamu bisa memanfaatkan fitur seperti Agents, Workflow, RAG, Memory, Tools, dan masih banyak lagi.</p><blockquote>🗣️“Lah, gituan juga ada bang di LangChain.”</blockquote><p>Yes and no, sini aku jelasin fitur-fitur penting yang bisa kamu pake buat bikin chatbot sekalian aku kasih tutorial simple untuk bikin chatbot pakai Mastra.</p><p>Sebelum masuk ke penjelasan, make sure bikin direktori untuk projectnya dan jalanin command ini untuk bikin project Mastra-nya</p><pre>bun create mastra</pre><p>Setelah itu tinggal ketik aja <strong>nama project</strong>nya apa, ketik dimana folder srcnya, pilih <strong>default AI provider</strong>, masukkin API key (bisa diskip) dan udah. Kalau mau simple, setelah ketik nama project bisa langsung enter-enter aja. Terakhir jangan lupa <strong>setup .env </strong>dan jalanin command ini untuk install semua dependency-nya.</p><pre>bun i</pre><p>Udah? yok aku jelasin perbedaannya!</p><h3>Apa Bedanya Bang?</h3><p>Walaupun kalau dibaca-baca terlihat dan terdengar sama, tapi sebenernya ada perbedaan yang cukup mencolok loh.</p><p><strong>Agents</strong></p><p>Mirip LangChain, Mastra AI juga punya fitur Agent yang memungkinkan kalian untuk membuat sebuah AI Agent yang bisa bertindak sesuai dengan prompt yang kalian kasih dan bisa menggunakan tools yang dia punya.</p><blockquote>🗣️“Lah kalo mirip apa bedanya bang?”</blockquote><p>Nah, bedanya adalah di LangChain kalian harus ‘ngerakit’ agentnya (modular), mulai dari OutputParser, PromptTemplate, Tools secara manual. Kalau di Mastra, tinggal pake aja, bener-bener tinggal pake karena udah terintegrasi!</p><p>Biar ga bingung, aku kasih contoh aja ya!</p><p>LangChain:</p><pre>import os<br>from langchain_openai import ChatOpenAI<br>from langchain_community.tools import DuckDuckGoSearchRun<br>from langchain.agents import AgentExecutor, create_openai_tools_agent<br>from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder<br>from langchain_core.tools import tool<br><br>@tool<br>def hitung_panjang_kata(kata: str) -&gt; int:<br>    &quot;&quot;&quot;Fungsi ini akan mengembalikan jumlah huruf dalam satu kata.&quot;&quot;&quot;<br>    return len(kata)<br><br>search_tool = DuckDuckGoSearchRun()<br><br>tools = [hitung_panjang_kata, search_tool]<br><br>llm = ChatOpenAI(model=&quot;gpt-4o-mini&quot;, temperature=0)<br><br>prompt_template = ChatPromptTemplate.from_messages([<br>    (&quot;system&quot;, &quot;You are a helpful weather assistant that provides accurate weather information and can help planning activities based on the weather.<br><br>      Your primary function is to help users get weather details for specific locations. When responding:<br>      - Always ask for a location if none is provided<br>      - If the location name isn&#39;t in English, please translate it<br>      - If giving a location with multiple parts (e.g. &quot;New York, NY&quot;), use the most relevant part (e.g. &quot;New York&quot;)<br>      - Include relevant details like humidity, wind conditions, and precipitation<br>      - Keep responses concise but informative<br>      - If the user asks for activities and provides the weather forecast, suggest activities based on the weather forecast.<br>      - If the user asks for activities, respond in the format they request.<br><br>      Use the weatherTool to fetch current weather data.&quot;),<br>    (&quot;human&quot;, &quot;{input}&quot;),<br>    MessagesPlaceholder(variable_name=&quot;agent_scratchpad&quot;)<br>])<br><br>agent = create_openai_tools_agent(llm, tools, prompt_template)<br><br>agent_executor = AgentExecutor(<br>    agent=agent,<br>    tools=tools,<br>    verbose=True # verbose=True akan mencetak proses &quot;berpikir&quot; agent<br>)<br><br>if __name__ == &quot;__main__&quot;:<br>    print(&quot;Agent siap! Silakan ajukan pertanyaan...&quot;)<br>    <br>    pertanyaan = &quot;Siapa presiden Indonesia saat ini, dan berapa panjang nama lengkapnya?&quot;<br>    <br>    # Kita &quot;memanggil&quot; (invoke) executor dengan input kita<br>    hasil = agent_executor.invoke({<br>        &quot;input&quot;: pertanyaan<br>    })<br>    <br>    print(&quot;\n--- HASIL AKHIR ---&quot;)<br>    print(hasil[&quot;output&quot;])</pre><figure><img alt="mY reaction pas liat python code" src="https://cdn-images-1.medium.com/max/640/0*LEvZcL7QJJBIW0_5.gif" /><figcaption>mY reaction pas liat kode di atas</figcaption></figure><p>Mastra:</p><pre>import { Agent } from &#39;@mastra/core/agent&#39;;<br>import { Memory } from &#39;@mastra/memory&#39;;<br>import { LibSQLStore } from &#39;@mastra/libsql&#39;;<br>import { weatherTool } from &#39;../tools/weather-tool&#39;;<br>import { scorers } from &#39;../scorers/weather-scorer&#39;;<br><br>export const weatherAgent = new Agent({<br>  name: &#39;Weather Agent&#39;,<br>  instructions: `<br>      You are a helpful weather assistant that provides accurate weather information and can help planning activities based on the weather.<br><br>      Your primary function is to help users get weather details for specific locations. When responding:<br>      - Always ask for a location if none is provided<br>      - If the location name isn&#39;t in English, please translate it<br>      - If giving a location with multiple parts (e.g. &quot;New York, NY&quot;), use the most relevant part (e.g. &quot;New York&quot;)<br>      - Include relevant details like humidity, wind conditions, and precipitation<br>      - Keep responses concise but informative<br>      - If the user asks for activities and provides the weather forecast, suggest activities based on the weather forecast.<br>      - If the user asks for activities, respond in the format they request.<br><br>      Use the weatherTool to fetch current weather data.<br>`,<br>  model: &#39;google/gemini-2.5-pro&#39;,<br>  tools: { weatherTool },<br>  scorers: {<br>    toolCallAppropriateness: {<br>      scorer: scorers.toolCallAppropriatenessScorer,<br>      sampling: {<br>        type: &#39;ratio&#39;,<br>        rate: 1,<br>      },<br>    },<br>    completeness: {<br>      scorer: scorers.completenessScorer,<br>      sampling: {<br>        type: &#39;ratio&#39;,<br>        rate: 1,<br>      },<br>    },<br>    translation: {<br>      scorer: scorers.translationScorer,<br>      sampling: {<br>        type: &#39;ratio&#39;,<br>        rate: 1,<br>      },<br>    },<br>  },<br>  memory: new Memory({<br>    storage: new LibSQLStore({<br>      url: &#39;file:../mastra.db&#39;, // path is relative to the .mastra/output directory<br>    }),<br>  }),<br>});</pre><p>Bedanya emang tipis sih kalau dilihat dari kodenya aja, tapi sebetulnya kelebihan yang dimiliki Mastra bukan cuma dari kodenya yang lebih simple, rapi, dan mudah dipahami. Letak kelebihannya justru berada di <strong>tool debugging </strong>dan <strong>visualisasi</strong>.</p><blockquote>️️️🗣️“Kalo cuma itu mah LangChain juga ada kali”</blockquote><p>Ya ga salah sih, tapi tapi tapiii, di Mastra ini tool debugging dan visualisasinya <strong>udah built-in </strong>loh! Kalian gaperlu lagi install tool tambahan seperti LangSmith, gaperlu daftar dulu, daftarin API keynya dulu, not so <em>demure 💅🏼. </em>Tinggal run command ini</p><pre>bun dev</pre><p>Habis itu udah deh, akses playground Mastra nya dan udah deh kalian bisa debugging dengan mudah tanpa harus install ini itu.</p><blockquote>🗣️“Wihh simple juga ternyata”</blockquote><p>Sesimple itu. Oh iya, kalian bisa atur kepribadian, <em>behaviour</em>, dan seberapa halu (<em>temprature</em>) AI Agent kalian di sini ya. Misal nih aku mau agentku lebih ramah, kalau ada user yang chat dia jawabnya pakai “kak”, tinggal ubah aja di bagian instruction. Ganti pake model lain? bisa banget, tinggal ganti aja dan jangan lupa untuk atur API key sesuai dengan model yang dipake. Tools yang dipake sama AI juga bisa diatur, mau ditambah, dikurangin, atau diatur urutan penggunaan toolsnya juga bisa.</p><p><strong>Workflow</strong></p><p>Sesuai sama artinya, workflow memungkinkan kalian untuk bisa memberi arahan atau langkah-langkah yang harus dilakukan sama si AI Agent kalian.</p><blockquote>🗣️“Workflow nih mirip sama chains kah? atau mirip sama workflow N8N bang?”</blockquote><p>Hampir mirip sama Chainsnya LangFuse dan mirip banget sama workflow N8N. Bedanya apa? dikutip dari Google Gemini 2.5 Pro Student Plan (hehe) Chainsnya si <strong>LangFuse</strong> ini lebih ke <strong>pipelining data</strong> dan butuh karakter “|” untuk bisa kalian debug pakai LangFuse, sedangkan <strong>Mastra</strong> lebih seperti <strong>mendefinisikan langkah-langkahnya seperti apa</strong>, mirip kayak ngajarin anak kecil <strong>step-by-step</strong> mengendarai sepeda.</p><blockquote>🗣️“Mirip N8N? bisa human-in-the-loop juga dong bang?”</blockquote><p>Ding ding ding…💯buat kamu. Bener banget di Mastra kita bisa kasih human-in-the-loop juga di workflownya.</p><pre>const fetchWeather = createStep({<br>  id: &#39;fetch-weather&#39;,<br>  description: &#39;Fetches weather forecast for a given city&#39;,<br>  inputSchema: z.object({<br>    city: z.string().describe(&#39;The city to get the weather for&#39;),<br>  }),<br>  stateSchema: z.object({<br>    geocodingResults: z<br>      .array(<br>        z.object({<br>          latitude: z.number(),<br>          longitude: z.number(),<br>          name: z.string(),<br>          country: z.string().optional(),<br>          country_code: z.string().optional(),<br>          admin1: z.string().optional(),<br>        }),<br>      )<br>      .optional(),<br>  }),<br>  suspendSchema: z.object({<br>    prompt: z.string(),<br>    options: z.array(<br>      z.object({<br>        index: z.number().int().min(0),<br>        label: z.string(),<br>      }),<br>    ),<br>  }),<br>  resumeSchema: z.object({<br>    selectedIndex: z.number().int().min(0),<br>  }),<br>  outputSchema: forecastSchema,<br>  execute: async ({ inputData, state, setState, suspend, resumeData }) =&gt; {<br>    if (!inputData) {<br>      throw new Error(&#39;Input data not found&#39;);<br>    }<br><br>    // Fetch and store geocoding results in state if not present<br>    let geocodingResults = state.geocodingResults;<br>    if (!geocodingResults) {<br>      const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(inputData.city)}&amp;count=10`;<br>      const geocodingResponse = await fetch(geocodingUrl);<br>      const geocodingData = (await geocodingResponse.json()) as {<br>        results: {<br>          latitude: number;<br>          longitude: number;<br>          name: string;<br>          country?: string;<br>          country_code?: string;<br>          admin1?: string;<br>        }[];<br>      };<br><br>      if (!geocodingData.results?.[0]) {<br>        throw new Error(`Location &#39;${inputData.city}&#39; not found`);<br>      }<br><br>      geocodingResults = geocodingData.results;<br>      setState({ ...state, geocodingResults });<br>    }<br><br>    // If multiple options and no selection yet, suspend for human choice<br>    if (geocodingResults.length &gt; 1 &amp;&amp; (resumeData?.selectedIndex === undefined || resumeData.selectedIndex === null)) {<br>      const options = geocodingResults.map((r, idx) =&gt; {<br>        const placeParts = [r.name, r.admin1].filter(Boolean).join(&#39;, &#39;);<br>        const countryPart = r.country ? `${r.country}${r.country_code ? ` (${r.country_code})` : &#39;&#39;}` : &#39;Unknown country&#39;;<br>        return { index: idx, label: `${placeParts ? `${placeParts}, ` : &#39;&#39;}${countryPart}` };<br>      });<br><br>      return suspend({<br>        prompt: `Multiple matches found for &#39;${inputData.city}&#39;. Please select the correct location:`,<br>        options,<br>      });<br>    }<br><br>    const selectedIndex = Math.max(0, Number(resumeData?.selectedIndex ?? 0));<br>    const selected = geocodingResults[selectedIndex] ?? geocodingResults[0];<br>    const { latitude, longitude, name, country } = selected;<br><br>    const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&amp;longitude=${longitude}&amp;current=precipitation,weathercode&amp;timezone=auto,&amp;hourly=precipitation_probability,temperature_2m`;<br>    const response = await fetch(weatherUrl);<br>    const data = (await response.json()) as {<br>      current: {<br>        time: string;<br>        precipitation: number;<br>        weathercode: number;<br>      };<br>      hourly: {<br>        precipitation_probability: number[];<br>        temperature_2m: number[];<br>      };<br>    };<br><br>    const forecast = {<br>      date: new Date().toISOString(),<br>      maxTemp: Math.max(...data.hourly.temperature_2m),<br>      minTemp: Math.min(...data.hourly.temperature_2m),<br>      condition: getWeatherCondition(data.current.weathercode),<br>      precipitationChance: data.hourly.precipitation_probability.reduce(<br>        (acc, curr) =&gt; Math.max(acc, curr),<br>        0,<br>      ),<br>      location: country ? `${name}, ${country}` : name,<br>    };<br><br>    return forecast;<br>  },<br>});</pre><p>Kode di atas adalah salah satu contoh workflow yang mengimplementasikan human-in-the-loop, dalam step di atas aku udah kasih “suspend” untuk ngepause jalannya workflow dan akan dilanjut menggunakan “resume” setelah user menjawab pertanyaan data cuaca di lokasi mana yang user inginkan.</p><blockquote>🗣️“Debuggingnya gimana bang? katanya ada visualisasinya?”</blockquote><p>Nihh aku kasih screenshotnya</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JvuPaSRTt_wnWznv6UIyNg.png" /><figcaption>Tampilan awal dari Playground Mastra</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wujYwStGLApgheCzE0t74A.png" /><figcaption>Chat Interface</figcaption></figure><p>Jangan diliat errornya plis, itu gara-gara API key geminiku udah habis free quotanya (kalau ada yang berbaik hati berbagi API key gemini gratis boleh dong hehe).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GKMCWAlt2-aI3Acio9Bj_w.png" /><figcaption>Interface Workflow</figcaption></figure><p>Dan masih banyak lagi sih, kalian bisa eksplor sendiri yes!</p><p><strong>Tools</strong></p><p>Tools ibarat tangan dan kakinya si AI Agent. Tanpa tools, AI cuma bisa halu karena dia gapunya alat apapun yang membantu dia untuk mencari informasi terkait.</p><p>Konsepnya sama juga seperti LangChain, tapi lagi lagi perbedaannya ada di <strong>developer experience<em> </em></strong>dan <strong>type validation</strong>.</p><blockquote>🗣️“Semua-semua sama bang, gimana sih”</blockquote><p>Ya sabar, di LangChain tools yaudah tools aja seperti biasa, tapi di Mastra kalian bisa mendefinisikan tool pakai function defineTool. Satu lagi, Mastra udah <strong>terintegrasi sama Zod Schema</strong>. Jadi, pas si LLM “berpikir” mau manggil tool kalian (misal getWeather(location: &quot;Jakarte&quot;)), Zod di Mastra bakal <em>auto-validate </em>inputnya. Kalau si LLM-nya lagi ngaco (misal getWeather(location: 123)), Mastra bakal nangkep itu sebelum fungsi kalian error ga jelas.</p><p>Contohnya ada di bawah ini!</p><pre>import { defineTool } from &#39;mastra&#39;;<br>import { z } from &#39;zod&#39;;<br><br>export const weatherTool = defineTool({<br>  schema: z.object({ <br>    city: z.string().describe(&quot;Nama kota yang mau dicek cuacanya&quot;) <br>  }),<br>  description: &quot;Mendapatkan info cuaca saat ini di kota tertentu.&quot;,<br>  <br>  async handler({ city }) {<br>    const response = await fetch(`https://api.cuaca.com/v1?q=${city}`);<br>    const data = await response.json();<br>    <br>    return data.currentWeather;<br>  }<br>});</pre><p>See? Dengan adanya Zod, kalian “maksa” si AI buat ngasih input yang bener sesuai skema. Gaperlu lagi tuh handling error if (typeof city !== &#39;string&#39;) atau if (!city) di dalem fungsi kalian. Si Mastra udah ngurusin di depan.</p><p><strong>Retrieval-Augmented Generation (RAG)</strong></p><p>Ini dia fitur yang bikin AI kalian pinter beneran, bukan cuma pinter halu. RAG ini intinya adalah teknik biar si AI bisa ngebaca dokumen kalian sendiri<strong> </strong>(PDF, TXT, file Notion, database) sebelum ngejawab. Jadi, jawabannya bisa sesuai fakta dari data kalian, bukan ngarang bebas dari data latihannya si LLM.</p><blockquote>🗣️“Halah, ini mah RAG. Di LangChain juga udah jadi makanan sehari-hari bang.”</blockquote><p>Yes betul sekali. Konsepnya emang itu-itu juga. Mau di LangChain, mau di Mastra, yang namanya RAG ya pasti prosesnya: Load<em> </em>-&gt; Split -&gt; Embed -&gt; Store -&gt; Retrieve.</p><blockquote>🗣️“NAH KAN SAMA AJA! Terus bedanya apaan dong?!”</blockquote><p>Bedanya lagi-lagi di kemudahan dan Unified API.</p><p>Di LangChain, kalian dikasih kotak Lego yang sebanyak itu. Ada loader buat PDF, loader buat CSV, loader buat YouTube, masing-masing kadang cara pakenya beda dikit. Terus ada 50 jenis vector store (Chroma, Pinecone, FAISS, dll) yang API-nya juga beda-beda. Fleksibel sih, tapi jujur… bikin pusing di awal.</p><p>Di <strong>Mastra</strong>, dia lebih fokus ke alur kerjanya. Mastra nyediain API yang lebih terpadu. Misalnya, dia punya UnifiedVectorStore API. Jadi, kalian belajar satu cara aja buat upsert atau query data, nanti di belakang layar mau ganti dari Chroma ke Pinecone gampang, kodenya ga banyak berubah.</p><p>Intinya, Mastra ga bikin kalian tenggelam dalam lautan pilihan. Dia ngasih jalur yang lebih jelas buat nge-RAG, dan integrasinya ke Agent atau Workflow-nya juga <strong>lebih seamless </strong>alias mulus.</p><p><strong>Memory</strong></p><p>Ini fitur wajib kalau kalian bikin chatbot. Masa tiap kali ngirim pesan baru, si AI-nya lupa kita lagi ngomongin apa. Pikun dong! Memory ini yang bertugas <strong>nyimpen riwayat obrolan</strong> biar konteksnya nyambung terus.</p><blockquote>🗣️“Eh bang, di contoh kode weatherAgent Mastra di atas, aku liat ada ginian:”</blockquote><pre>memory: new Memory({<br>    storage: new LibSQLStore({<br>      url: &#39;file:../mastra.db&#39;, // path is relative to the .mastra/output directory<br>    }),<br>  }),</pre><blockquote>🗣️“Itu kan?! LangChain juga punya ConversationBufferMemory, ConversationSummaryMemory, dll. Trus apa bedanya bang?”</blockquote><p>Emang mirip sih, tapi memory LangChain sama memory Mastra itu beda. Di LangChain, memory itu objek terpisah yang harus kalian manage dan suntikkin secara manual ke dalam AgentExecutor kalian. Kalian juga harus pastiin prompt kalian punya placeholder yang bener.</p><p>Di <strong>Mastra</strong>, memory itu <strong>bagian <em>native</em> dari definisi Agent</strong>. Liat aja kode di atas. Kalian tinggal deklarasiin memory: new Memory(...) pas bikin agent. Selesai. Ga perlu pusing inject sana-sini. Si <em>agent</em> Mastra otomatis tau cara pake memory-nya. Lebih rapi, lebih integrated.</p><p>Gak cuma itu, memory Mastra juga canggih. Dia ga cuma nyimpen obrolan terakhir (kayak BufferMemory). Dia punya konsep built-in kayak:</p><ul><li><strong>Working Memory:</strong> Ingatan jangka pendek buat tugas yang lagi dikerjain si agent.</li><li><strong>Semantic Recall:</strong> Ini gokil sih. Ini ingatan jangka panjang. Si agent bisa nyimpen fakta penting dari obrolan kalian ke vector store. Jadi, kalau sebulan lagi kalian chat, dia bisa inget fakta itu (misal: “Oh iya, si Reza kan sukanya kopi Gayo, bukan Robusta”).</li></ul><p>Jadi, bedanya:</p><ul><li><strong>LangChain:</strong> Memory itu tambahan yang harus kalian pasang dan manage sendiri.</li><li><strong>Mastra:</strong> Memory itu fitur bawaan yang udah terintegrasi erat sama si agent.</li></ul><p><strong>Kesimpulan</strong></p><p>Selesai sudah pembahasan terkait apa sih bedanya LangChain sama Mastra.</p><blockquote>🗣️“Itu aja bang?”</blockquote><p>Ya sementara itu dulu sih, sebenernya masih banyak lagi fitur-fitur di Mastra yang bisa dihighlight seperti voice interaction yang memungkinkan kalian untuk bikin chatbot yang bisa diajak ngobrol pake suara beneran, bukan cuma ketik ketik aja.</p><blockquote>🗣️“Kalo dipikir-pikir emang lebih enak di Mastra sih, debuggingnya lebih simple dan gaperlu daftar ini itu”</blockquote><p>Yes, kelebihan dari Mastra yang udah aku sebutin dari awal sampai akhir ini adalah <strong>Developer Experience</strong>-nya. Mastra <strong>memanjakan developer </strong>dengan playground dan debugging feature yang built-in biar developer bisa fokus sama Mastra aja, gaperlu lagi install tools tambahan <strong>hanya untuk debugging dan visualisasi</strong>. Satu lagi, ini <strong>pake TypeScript</strong>, jadi lebih <strong>kenceng</strong>, lebih <strong>type-strict</strong> juga kalau dibanding pakai Python (setidaknya itu sih yang dibanggakan sama yang suka TypeScript).</p><blockquote>🗣️”Jadi bingung bang, enaknya pake LangChain atau Mastra ya?”</blockquote><p>Kalau kalian butuh <strong>environment debugging<em> </em></strong>yang built-in, gamau ribet install ini itu cuma buat debugging ya pake <strong>Mastra</strong> aja, tapi kalau cari <strong>dukungan library</strong> yang lengkap banget, dan butuh <strong>fleksibilitas</strong> saat develop pake aja <strong>LangChain</strong>.</p><p>Btw bukannya tadi bilang cuma mau ngoding TypeScript ya? gitu masih bingung mau pake apa.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/543/1*v0LqdGNPfbD5NBRRSuVtvQ.png" /></figure><p>Udah ada niat buat bikin chatbot sendiri? Gas, pake Mastra aja!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=db4f78422d46" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/bikin-chatbot-pakai-mastra-ai-atau-langchain-db4f78422d46">Bikin Chatbot Pakai Mastra.AI Atau LangChain?</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why Innovation Programs Fail — And What UX has to Do with It]]></title>
            <link>https://medium.com/dot-intern/why-innovation-programs-fail-and-what-ux-has-to-do-with-it-442a59bd8aa8?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/442a59bd8aa8</guid>
            <category><![CDATA[ux-research]]></category>
            <category><![CDATA[innovation]]></category>
            <category><![CDATA[ux]]></category>
            <category><![CDATA[human-resources]]></category>
            <category><![CDATA[learning-and-development]]></category>
            <dc:creator><![CDATA[keisha ale]]></dc:creator>
            <pubDate>Fri, 05 Dec 2025 14:15:39 GMT</pubDate>
            <atom:updated>2025-12-05T14:15:38.186Z</atom:updated>
            <content:encoded><![CDATA[<h3>Why Innovation Programs Fail — And What UX has to Do with It</h3><p><strong><em>Reframing Innovations Programs Through the Lens of User Experience</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/735/1*qq2SGOXO86xDkX7ZfztSRw.jpeg" /><figcaption>cr. Pinterest</figcaption></figure><p><strong><em>When employees are less motivated to participate in innovation programs, it may not be about the idea , but about the experience.</em></strong></p><p>Perusahaan sangat menyukai inovasi. Mungkin karena inovasi memiliki beberapa manfaat untuk perusahaan dan bisa dijadikan solusi untuk meningkatkan keterlibatan dari manusia-manusia yang ada di dalamnya. Tetapi ketika perusahaan memberikan wadah kepada para karyawan untuk berinovasi, angka partisipan mengatakan yang sebaliknya seperti sedikit yang mendaftar, sedikit diskusi, dan kurangnya <em>excitement</em> dalam merespon program inovasi.</p><p>Apa yang salah?</p><p>Ada beberapa karyawan yang tidak bergabung karena mereka tidak punya waktu atau tidak punya ide. Tetapi bagaimana jika masalahnya adalah bagaimana pengalaman karyawan ketika berinovasi itu dirancang?</p><p><strong><em>Innovation as a User Journey</em></strong></p><p><em>Let’s take a look from User Experience (UX)’s lens</em>.</p><p>Di dalam UX, desain dirancang untuk <em>user</em> bukan dari sistem. Timbul pertanyaan utama ini,</p><blockquote>“ Apa yang memotivasi mereka?”</blockquote><blockquote>“Dimana mereka mengalami suatu hambatan?”</blockquote><blockquote>“Emosi apa yang mereka rasakan selama proses?”</blockquote><p>Pertanyaan-pertanyaan itu akan menjadi dasar ketika prinsip-prinsip UX akan diterapkan ke program inovasi. <em>User </em>di sini adalah karyawan dan <em>journey</em> adalah ketika mereka mendengar bahwa ada program inovasi, dilanjutkan ketika mereka melakukan <em>brainstorm</em>, berkolaborasi, mengumpulkan proposal, dan melihat ide-ide mereka diimplementasikan secara nyata.</p><p>Apabila suatu saat mereka merasa bahwa proses ini membuat mereka merasa frustasi, mengintimidasi, atau tidak relevan buat mereka, maka mereka akan mengundurkan diri.</p><p><strong><em>Case Study: When The Engagement Drops</em></strong></p><p>Di DOT Indonesia, terdapat sebuah program inovasi bernama <em>Innovation Award</em>. Di tahun 2025, program inovasi tersebut mengalami penurunan partisipan. Dari yang awalnya ada 21 peserta di tahun 2024 menjadi 10 peserta di tahun ini.</p><p>Survei karyawan menunjukkan:</p><blockquote>“Tidak ada waktu.”</blockquote><blockquote>“Tidak tahu harus berinovasi tentang apa. Tidak ada ide.”</blockquote><blockquote>“Ada kendala di proses pengerjaannya. Setiap ide harus terselesaikan.”</blockquote><blockquote>“Kurangnya <em>support</em>.”</blockquote><p>Ketika memetakan pengalaman karyawan dari perspektif UX menunjukkan bahwa:</p><ul><li><em>Time Friction — misaligned context of use</em></li></ul><p>“Tidak ada waktu.”</p><p>Program belum disesuaikan dengan ritme kerja karyawan sehingga untuk berpartisipasi terasa seperti beban tambahan bukan bagian dari alur kerja alami.</p><ul><li><em>Ideation Friction — lack of scaffolding for creativity</em></li></ul><p>“Tidak ada ide.”</p><p>Karyawan ingin berkontribusi tapi belum tau proses menemukan ide yang relevan dengan tema. Hal itu mungkin disebabkan karena tidak ada ruang eksplorasi bersama dan tidak ada alat bantu seperti <em>ideation canvas </em>atau media lain yang bisa memicu pemikiran.</p><ul><li><em>Process Friction — rigid system and high completion demand</em></li></ul><p>“Ada kendala di proses pengerjaannya, jadi setiap ide harus terselesaikan.”</p><p>Desain proses terlalu kaku dan berorientasi pada <em>output-driven</em> bukan eksplorasi ide sehingga akan menciptakan tekanan kognitif dan menghambat rasa aman untuk bereksperimen.</p><ul><li><em>Emotional Friction — lack of psychologycal safety &amp; support</em></li></ul><p>“Kurangnya <em>support.</em>”</p><p>Karyawan merasa tidak ada dukungan atau apresiasi yang cukup dari manajemen maupun perusahaan. Rasa takut gagal akhirnya menurunkan motivasi untuk berpartisipasi.</p><p><strong><em>Designing Innovation Like a Product</em></strong></p><p>Melakukan pendekatan terhadap program inovasi akan membuat perusahaan terjadi peningkatan melalui <em>feedback</em> dan <em>testing</em>.</p><ul><li><em>Emphathize</em></li></ul><p>Melakukan survei singkat untuk memahami bagaimana karyawan mengalami program inovasi dalam konteks kerja sehari-hari. Berfokus pada momen-momen dimana ada munculnya keterbatasan waktu, kebingungan, atau kurang percaya diri.</p><ul><li><em>Define</em></li></ul><p>Memetakan titik-titik friksi utama sepanjang perjalanan partisipasi dalam program inovasi.</p><ul><li><em>Ideate</em></li></ul><p>Menciptakan solusi bersama antara karyawan dan pengelola program seperti:</p><ol><li>Menyederhanakan alur pengajuan ide atau memungkinkan bentuk partisipasi kecil.</li><li>Menyediakan alat bantu terstruktur untuk berpikir kreatif.</li><li>Membangun <em>feedback loop</em> dan sistem mentoring untuk meningkatkan rasa aman.</li><li>Menanamkan elemen apresiasi di setiap tahap, bukan hanya akhir program.</li></ol><ul><li><em>Prototype</em></li></ul><p>Uji coba inisiatif skala kecil yang menargetkan friksi tertentu, contohnya tantangan inovasi di tingkat divisi dengan durasi lebih singkat sehingga melibatkan <em>leader</em> dan <em>manager</em>.</p><ul><li><em>Test &amp; Iterate</em></li></ul><p>Mengumpulkan umpan balik dan ukur metrik pengalaman seperti:</p><ul><li>Kemudahan partisipasi</li></ul><p>“Seberapa mudah prosesnya dijalani?”</p><ul><li>Persepsi Dukungan</li></ul><p>“Apakah peserta merasa didampingi dan diarahkan?”</p><ul><li>Keterlibatan Emosional</li></ul><p>“Apakah program terasa memotivasi atau justru menekan?”</p><p><strong><em>Key Point Takeaways</em></strong></p><p><strong>UX Prinsiple → Aplikasi</strong></p><ul><li><em>User Research </em>→ Memahami motivasi dan kendala karyawan.</li><li><em>Journey Mapping</em> → Mengidentifikasi hambatan di berbagai titik karyawan.</li><li><em>Prototyping</em> → Menguji format baru sebelum melakukan <em>Innovation Awards</em>.</li><li><em>Emotional Design</em> → Membangun keamanan psikologis dan mengapresiasi segala bentuk progres.</li><li><em>Feedback Loops</em> → Menjaga transparasi melalui komunikasi.</li></ul><p><strong><em>Reframing innovation programs through the lens of User Experience offers a practical yet human-centered approach to revitalizing participation.</em></strong></p><p>Ketika karyawan merasa bahwa perjalanan mereka didukung dan bermakna, mereka cenderung terlibat kreatif. Intinya, inovasi akan berkembang dan <em>engagement</em> akan meningkat tidak hanya melalui proses tetapi melalui pengalaman yang dirancang dengan empati, kejelasan, dan tujuan.</p><blockquote><strong><em>Design for people first — innovation will follow.</em></strong></blockquote><p>Referensi:</p><p>Hassenzahl, M., &amp; Tractinsky, N. (2006). User Experience — a research agenda. DOI: 10.1080/01449290500330331</p><p>Sutcliffe, A. (2016). Designing for User Experience and Engagement. <a href="https://doi.org/10.1007/978-3-319-27446-1_5">https://doi.org/10.1007/978-3-319-27446-1_5</a></p><p>Interaction Design. Innovation in UX/UI Design. <a href="https://www.interaction-design.org/literature/topics/innovation?srsltid=AfmBOoprlYdclNCYYs5M4HlI4nLHhqsw5TCumtp4MYT9AsObCYSUAqzm#1._examine_the_most_avenues_in_their_design_process-3">https://www.interaction-design.org/literature/topics/innovation?srsltid=AfmBOoprlYdclNCYYs5M4HlI4nLHhqsw5TCumtp4MYT9AsObCYSUAqzm#1._examine_the_most_avenues_in_their_design_process-3</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=442a59bd8aa8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/why-innovation-programs-fail-and-what-ux-has-to-do-with-it-442a59bd8aa8">Why Innovation Programs Fail — And What UX has to Do with It</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ANTARA INOVASI DAN INTERUPSI: BAGAIMANA MENYIKAPI PERKEMBANGAN AI?]]></title>
            <link>https://medium.com/dot-intern/antara-inovasi-dan-interupsi-bagaimana-menyikapi-perkembangan-ai-bb8d5e1038ab?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/bb8d5e1038ab</guid>
            <dc:creator><![CDATA[Ilhami Lailatul Safitri]]></dc:creator>
            <pubDate>Mon, 01 Dec 2025 04:22:11 GMT</pubDate>
            <atom:updated>2025-12-01T04:22:10.933Z</atom:updated>
            <content:encoded><![CDATA[<p>Beberapa waktu terakhir perkembangan teknologi sangat melaju pesat. Hasil dari kemajuan teknologi ini salah satunya melahirkan AI yang hari-hari ini tampaknya sering lewat di beranda kita di segala platform media sosial. Tapi jika di tarik beberapa waktu lalu dimasa AI ini pertama kali muncul, banyak sekali perdebatan akan kemunculannya. Mulai dari penyebab pengangguran, orisinalitas karya yang dipertanyakan hingga interupsi AI dalam pekerjaan. Namun, saat ini AI bisa menjadi salah satu tools dalam bekerja untuk memaksimalkan hasil pekerjaan kita. Tentu saja tetap mempertimbangkan keorisinalitasan karya yang kita buat dan penggunaan AI dengan cara yang bijak. Penting sekali untuk bisa memanfaatkan AI sebagai tools untuk meningkatkan produktivitas dengan bijak dan juga tetap memberikan credit setiap menggunakan AI untuk keterbukaan hasil karya yang kita hasilkan.</p><p>Masuk ke dunia magang dan profesional di salah satu agency membuat aku banyak belajar juga, dimana penggunaan AI memang sudah dilakukan di banyak bidang dan penggunaannya untuk mendukung produktivitas kerja. Sebagai video editor, aku juga punya banyak akses untuk bisa belajar dan mengetahui banyak tipe dan jenis AI yang sudah ada dan membuat aku sadar ternyata perkembangan AI luar biasa cepat. Aku akan share beberapa tools AI yang selama magang di posisi video editor sering ngebantu pengerjaan task aku.</p><ol><li><strong>Hygen.com</strong></li></ol><p>Hygen ini jadi tools AI pertama yang aku gunain saat baru banget masuk menjadi video editor. Saat itu aku sudah tau kalo AI itu bisa mengubah gambar menjadi video, tapi saat tau cara pengaplikasian lebih detailnya justru aku jadi <em>amazed </em>karena ternyata AI itu sudah berkembang sangat pesat. Pada dasarnya Hygen ini salah satu website AI yang bisa digunain untuk membuat video yang bahkan suaranya bisa kita ganti-ganti sesuai dengan logat atau nada yang kita butuhkan. Bahkan, hasilnya bener-bener realistis karena kelihatan seperti manusia asli. Enggak sampe situ aja, Hygen ini juga bisa membuat avatar yang kita mau sampe customize ekspresi wajah yang mau ditampilin saat berbentuk video nanti, <em>style </em>baju sampe <em>movement </em>objeknya nanti. Nah, Hygen ini aku gunakan kalo butuh <em>footage </em>atau objek yang berbicara gitu tapi gausah untuk take-take lagi karena hanya perlu ganti<em> prompt script</em> yang diinginkan.</p><p><strong>2. Lovable.dev</strong></p><p>Kebayang ga bisa punya web sendiri sesuai yang kita mau hanya dalam hitungan menit? Lovable bisa banget buat bikinin web yang sesuai keinginan kita. Bukan cuman <em>website</em>, Lovable bisa banget buat bikinin kamu aplikasi juga loh cuman modal chatting dengan AI dan <em>describe style website</em> atau aplikasi yang kita butuhin dan langsung jadi. Menurut aku, sejauh aku belajar saat magang, ini jadi salah satu AI paling keren karena beneran bisa <em>website </em>cuman dari satu kali klik aja.</p><p><strong>3. Lovart.ai</strong></p><p>Kalo ini cocok banget buat cari inspirasi dan <em>style </em>untuk mendesain sesuatu. <em>Simply </em>kayak kita punya designer pribadi, Lovart.ai bisa kasih masukan design ke konsep yang kita kasih ke AI ini. Di Lovart.ai ini kita bisa buat <em>storyboard </em>dan <em>brainstorming </em>sama AI nya untuk nentuin <em>style </em>yang cocok sampe kasih saran untuk estetikanya. Tapi Lovart.ai ga cuman buat design 2D, AI ini juga bisa banget untuk buat design berupa video, 3D dan bentuk-bentuk lainnya. Jadi, kalo butuh temen diskusi untuk konsep dan ide design bisa banget buat pake Lovart.ai buat optimalisasi kerjaan kalian.</p><p><strong>4. Higgsfield</strong></p><p>Ini mungkin jadi tools AI yang paling <em>align </em>sama posisi video editor ya karena secara sederhana ini AI yang bisa bikin <em>visual effect</em> ke video atau foto yang kita <em>import </em>ke <em>website </em>Higgsfield. Kelebihan dari Higgsfield ini karena pilihan efeknya beragam banget dan kece juga bikin tampilan video kita lebih <em>engaging </em>dan jadi salah satu daya tarik juga. Karena pilihannya banyak banget jadi bisa disesuaikan sama kebutuhan untuk marketing, edukasi, atau <em>entertaining video.</em></p><p>Nah, jadi itu top 4 <em>tools </em>AI yang pernah aku pake selama kegiatan magang aku di video editor. Tentunya masih banyak sekali tools AI yang sudah berkembang lebih jauh diluar sana, tapi 4 list diatas merupakan sedikit <em>tools </em>yang aku bisa sebutkan. Terlepas dari perdebatan penggunaan AI diluar sana, aku yakin perkembangan teknologi menjadi salah satu hal yang tidak bisa kita cegah, termasuk AI. Dengan kita menutup mata dengan perkembangan teknologi, semakin kita akan merasa tertinggal dan akan kehilangan banyak kesempatan juga. Di sisi lain, AI juga bisa loh untuk mengoptimalkan kinerja kita selama digunakan dengan bijak dan sesuai takaran tanpa menghilangkan esensi karya yang mau kita buat. Perkembangan teknologi yang masif juga mewajibkan kita untuk harus pintar tanpa ketinggalan zaman karena perkembangan AI ini banyak hal yang bisa terkesan “digantikan” dengan keberadaannya. Namun, kita juga harus lebih cermat menggunakan dan pandai untuk memaksimalkan perkembangan AI sehingga kemampuan kita tetap bisa eksis dan bisa bersaing juga dengan AI tanpa menolak keberadaan AI melainkan mengoptimalkan perkembangannya.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bb8d5e1038ab" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/antara-inovasi-dan-interupsi-bagaimana-menyikapi-perkembangan-ai-bb8d5e1038ab">ANTARA INOVASI DAN INTERUPSI: BAGAIMANA MENYIKAPI PERKEMBANGAN AI?</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kenapa Nggak Pernah Bosan Magang di DOT — dari Seseorang yang Sudah Pernah Magang Dua Kali]]></title>
            <link>https://medium.com/dot-intern/kenapa-nggak-pernah-bosan-magang-di-dot-dari-seseorang-yang-sudah-pernah-magang-dua-kali-4069b655e2e3?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/4069b655e2e3</guid>
            <dc:creator><![CDATA[Tony I.]]></dc:creator>
            <pubDate>Fri, 28 Nov 2025 16:12:10 GMT</pubDate>
            <atom:updated>2025-11-28T16:12:10.307Z</atom:updated>
            <content:encoded><![CDATA[<h3>Kenapa Nggak Pernah Bosan Magang di DOT — dari Seseorang yang Sudah Pernah Magang Dua Kali</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YsbrLz30kQptBkjPrcsq3w.jpeg" /></figure><p>Banyak orang menganggap magang cukup sekali — sekadar formalitas buat nambah pengalaman sebelum lulus. Tapi buat aku, pengalaman magang di DOT justru bikin pengen balik lagi. Lucunya, setelah magang pertama aku di DOT, aku sempat kerja sebagai profesional full-time di luar sana. Waktu itu aku ngerasa udah cukup paham soal ritme kerja, tanggung jawab, dan dunia profesional yang sebenarnya. Aku pikir, fase magang udah selesai di hidupku. Tapi ternyata, waktu kesempatan buat balik ke DOT datang lagi, aku langsung ambil tanpa mikir panjang. Rasanya kayak pulang ke rumah kedua — tempat yang udah akrab, tapi selalu punya hal baru buat dieksplor.</p><p>Balik magang setelah sempat kerja profesional tuh rasanya unik. Di satu sisi, aku udah punya pengalaman kerja yang lebih matang. Tapi di sisi lain, aku sadar kalau magang bukan sekadar “posisi sementara,” melainkan ruang buat terus belajar tanpa tekanan berlebihan. DOT justru jadi tempat di mana aku bisa menyeimbangkan dua hal itu — profesionalitas dan rasa ingin tahu.</p><p>Sebagai graphic designer, hal yang paling aku syukuri dari DOT adalah kebebasan berkreasi. Di sini aku nggak dibatasi oleh aturan perusahaan yang kaku. Aku bebas nyoba layout baru, gaya ilustrasi yang berbeda, atau eksplorasi warna yang mungkin di tempat lain dianggap “nggak sesuai guideline.” Setiap ide diterima dengan terbuka, bahkan yang awalnya kelihatan nyeleneh pun bisa jadi titik awal sesuatu yang menarik. DOT ngasih ruang buat bereksperimen, dan itu bikin proses kerja terasa jauh lebih hidup.</p><p>Yang bikin aku betah tentu juga orang-orangnya. Teman-teman yang supportif, selalu siap bantu, saling backup satu sama lain. Mentornya juga humble dan terbuka — nggak cuma ngarahin, tapi juga ngajak diskusi. Rasanya lebih kayak kolaborasi daripada bimbingan satu arah. Karena itu, setiap tugas terasa ringan. Capek sih pasti ada, tapi capek yang dibarengi rasa fun itu beda — nggak berat. Makanya, selama di DOT, aku ngga pernah ngerasa adanya tekanan selama baik itu ngerjain kerjaan atau diskusi bareng tim.</p><p>Satu lagi hal yang aku suka dari DOT itu di mana nggak ada jarak berlebihan antara intern, karyawan, atau mentor. Semua orang punya ruang buat didengar. Buat aku, hal sesederhana itu justru bikin suasana kerja terasa nyaman. Aku jadi lebih berani buat ngomong, ngasih ide, atau bahkan nanya hal-hal kecil tanpa takut salah. Terdengar sederhana, namun lingkungan yang seperti ini nggak mudah ditemukan di banyak tempat.</p><p>Dari dua kali magang ini, aku belajar kalau tempat kerja yang baik bukan cuma soal proyek besar atau fasilitas lengkap. Tapi tentang ruang buat tumbuh, dukungan dari orang-orang di sekitarnya, dan rasa nyaman buat jadi diri sendiri. Di DOT, semua itu ada.</p><p>Magang di DOT bukan cuma soal belajar desain, tapi juga belajar berproses — gimana menikmati pekerjaan, menghargai ide orang lain, dan tetap kreatif. Aku juga belajar bahwa “belajar” nggak berhenti meskipun udah sempat kerja profesional. Kadang, justru dengan balik ke tempat yang bikin nyaman, kita bisa lihat seberapa banyak diri kita berkembang.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4069b655e2e3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/kenapa-nggak-pernah-bosan-magang-di-dot-dari-seseorang-yang-sudah-pernah-magang-dua-kali-4069b655e2e3">Kenapa Nggak Pernah Bosan Magang di DOT — dari Seseorang yang Sudah Pernah Magang Dua Kali</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Manajemen Branch Git yang Efektif: Dari Task Baru Hingga Siap di Review]]></title>
            <link>https://medium.com/dot-intern/manajemen-branch-git-yang-efektif-dari-task-baru-hingga-siap-di-review-87114c6a673b?source=rss----20935cb0cc37---4</link>
            <guid isPermaLink="false">https://medium.com/p/87114c6a673b</guid>
            <category><![CDATA[gitlab-merge-request]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Rinooktavianridwan]]></dc:creator>
            <pubDate>Wed, 19 Nov 2025 09:46:39 GMT</pubDate>
            <atom:updated>2025-11-19T09:46:38.968Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MetP4Z1EriO4w5Swk3ZA6w.png" /></figure><p>Pernah merasa progres sudah jauh, tetapi merge request (MR) mentok karena konflik, commit berisi “fix kode” yang tidak deskriptif, atau judul MR yang tidak menjelaskan apa pun?</p><p>Di masa magang, saya pernah mengalaminya. Pelan-pelan, saya belajar bahwa manajemen branch yang rapi bukan soal gaya, melainkan fondasi kolaborasi: mulai dari sinkronisasi awal, penamaan branch yang jelas, strategi push yang tertata, sampai menulis MR yang nyaman untuk ditinjau. Artikel ini merangkum praktik harian yang saya pakai secara ringkas dan mudah diterapkan.</p><p><strong>Persiapan: Sinkronisasi &amp; Penamaan Branch <br>1. Sinkronisasi sebelum memulai<br></strong>Salah satu penyebab utama konflik saat Merge Request (MR) adalah memulai pekerjaan dari basis kode yang sudah usang. Tanpa sadar, kita mengerjakan fitur di atas versi proyek yang sudah ketinggalan, sementara rekan tim lain mungkin sudah menambahkan pembaruan penting di branch utama (main atau develop).</p><p>Satu langkah preventif yang wajib dilakukan sebelum menulis kode adalah menyelaraskan repository lokal dengan versi terbaru yang ada di server (GitLab).</p><p>Caranya adalah dengan berpindah ke branch utama dan menjalankan perintah git pull.</p><pre># Pastikan berada di branch utama<br>git switch main<br><br># Tarik semua perubahan terbaru dari server<br>git pull origin main</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/541/1*pyV-rOOgiZBSe6XEEDnalg.png" /><figcaption>sinkronisasi sebelum memulai</figcaption></figure><p>Perintah git pull diatas memastikan kita mengambil semua pembaruan dari server dan menerapkannya di proyek lokal. Dengan memulai dari “garis start” yang sama dengan semua orang, kita dapat mengurangi risiko konflik di kemudian hari.</p><p><strong>2. Penamaan Branch yang Informatif<br></strong>Setelah basis kode sinkron, langkah selanjutnya adalah menciptakan branch(ruang kerja) baru untuk task yang akan dikerjakan. Nama branch yang buruk seperti “fitur-baru” atau “perbaikan” tidak memberikan konteks apa pun kepada tim. Sebaliknya, praktik terbaik adalah mengintegrasikan informasi dari issue board (seperti GitLab Issues) langsung ke dalam nama branch.</p><p>Saran format yang baik untuk penamaan branch seperti berikut:<br><strong>tipe/nomor-isu-deskripsi-singkat<br></strong>keterangan :</p><ul><li>tipe: Menjelaskan tujuan branch (misalnya: feature, fix, refactor, adjust)</li><li>nomor-isu: Nomor tiket dari task yang sedang dikerjakan</li><li>deskripsi-singkat: Beberapa kata yang merangkum task tersebut</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c0xxTRP3wOuKyar9TAmJkw.png" /><figcaption>contoh issue gitlab</figcaption></figure><p>Gambar diatas adalah contoh card issue yang ingin dikerjakan, sehingga pembuatan branch baru dapat dinamakan <strong>adjust/1-update-deskripsi-readme</strong></p><p><strong>3. Perintah Modern untuk Berpindah Branch<br></strong>Setelah memiliki nama branch yang ideal, saatnya untuk membuat dan langsung berpindah ke dalamnya. Untuk itu, kita bisa menggunakan perintah git switch dengan flag -c (singkatan dari create).</p><pre># Membuat dan langsung berpindah ke branch baru <br>git switch -c adjust/1-perbarui-readme</pre><p>Apa bedanya dengan git checkout -b?</p><p>Kita mungkin lebih sering melihat perintah git checkout -b digunakan untuk tujuan yang sama. Keduanya memang menghasilkan output yang identik. Namun, git switch diperkenalkan di versi Git yang lebih baru (2.23) untuk memisahkan fungsi.</p><p>Perintah git checkout di versi lama memiliki dua fungsi utama yang sangat berbeda:</p><ul><li>Berpindah branch (contoh: git checkout nama-branch).</li><li>Mengembalikan perubahan pada file (contoh: git checkout — nama-file.txt).</li></ul><p>Karena fungsi gandanya bisa menimbulkan ambiguitas, git memperkenalkan dua perintah yang lebih spesifik untuk mengatasi hal tersebut :</p><ul><li>git switch: Didesain khusus hanya untuk berpindah atau membuat branch.</li><li>git restore: Didesain khusus hanya untuk mengembalikan file.</li></ul><p>Menggunakan git switch membuat niat lebih eksplisit dan jelas, sekaligus menerapkan praktik modern yang mengurangi ambiguitas dan risiko kesalahan. Setelah menjalankan perintah, kita akan berada di branch yang baru dan aman.</p><p><strong>Pengerjaan: Workflow &amp; Perintah Git Saat Pengembangan<br>1. Pesan Commit yang Jelas dan Informatif</strong></p><p>Pesan commit yang buruk seperti “fix bug” atau “update code” mungkin cukup, tetapi ketika ada orang lain yang mencoba memahami riwayat perubahan akan mengalami kesulitan.</p><p>Praktik yang baik adalah mengikuti sebuah format sederhana untuk konsistensi. Salah satu yang paling populer dan mudah diadopsi adalah <em>Conventional Commits</em>. Format dasarnya adalah:<br><strong>tipe: deskripsi singkat<br></strong>keterangan :</p><ul><li>tipe: Satu kata yang mengkategorikan perubahan yang dilakukan seperti: feat, fix, adjust, dst.</li><li>deskripsi singkat: Kalimat ringkas yang menjelaskan apa yang dilakukan.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Mo9XxMdLndahwFBvSr_0Mw.png" /></figure><p>Pesan commit yang informatif mengubah git log dari sekadar daftar perubahan acak menjadi cerita yang terstruktur. Dampaknya, proses code review lebih mudah, penelusuran sumber bug di masa depan lebih cepat, dan changelog (catatan perubahan) untuk rilis baru bisa dihasilkan secara otomatis.</p><p><strong>2. Simpan Perubahan Sementara(Stash)<br></strong>Seringkali saat mengembangkan fitur, kita berada di persimpangan jalan. Mungkin kode yang kita tulis sudah berjalan, tetapi kita merasa logikanya kurang efisien dan ingin mencoba pendekatan yang sama sekali berbeda. Masalahnya, kita tidak ingin membuang perubahan yang sudah ada, tetapi juga belum siap untuk menyimpannya sebagai commit.</p><p>Selain itu, skenario lain yang umum terjadi adalah ketika datang permintaan darurat (misalnya perbaikan bug) di salah satu fitur, padahal pekerjaan saat ini belum selesai dan tidak bisa di-commit.</p><p>Dalam kedua situasi tersebut, git stash berperan sebagai laci penyimpanan sementara. Perintah ini memungkinkan kita untuk menyimpan semua perubahan yang belum di-commit dengan aman, dan mengembalikan kondisi proyek ke keadaan commit terakhir.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*53nm377zjAm63apZnrX5kg.png" /><figcaption>contoh penggunaan git stash</figcaption></figure><p>Mari kita bedah perintah-perintah yang digunakan pada contoh di atas:</p><ul><li>git stash push -m “WIP: tambah bagian Catatan di README” : Menyimpan perubahan yang belum di-commit ke dalam “laci” sementara dengan pesan deskriptif.</li><li>git stash list : Menampilkan daftar semua simpanan (stash) yang ada.</li><li>git stash show stash@{0}: Mengintip ringkasan perubahan di dalam stash tanpa mengeluarkannya.</li><li>git stash pop stash@{0} : Menerapkan perubahan dari stash dan langsung menghapusnya dari daftar. Sebagai alternatif, bisa menggunakan git stash apply stash@{0} yang akan menerapkan perubahan tanpa menghapusnya dari daftar.</li></ul><p>git stash memberi fleksibilitas untuk bisa bereksperimen dengan aman atau menangani interupsi tanpa harus membuat commit “setengah jadi” (WIP). Sehingga dapat menjaga riwayat commit tetap bersih dan hanya berisi perubahan-perubahan yang sudah final.</p><p><strong>Penyerahan Perubahan: Dari Push hingga Merge Request<br>1. Push Kode ke Remote<br></strong>Sebelum membagikan hasil kerja ke tim, ada baiknya melakukan satu sinkronisasi terakhir. Ini untuk memastikan kita tidak melewatkan pembaruan apa pun yang mungkin masuk ke branch utama (main) saat sedang mengerjakan task.</p><p>Jalankan git pull sekali lagi pada branch utama untuk memastikan semuanya selaras. Setelah yakin tidak ada yang terlewat dan semua perubahan sudah di commit, saatnya untuk mengirimkan (push) branch dari komputer lokal ke server GitLab.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ghqxcRMpTdyhWqoQe215BA.png" /></figure><p>Dengan selalu menuliskan nama remote (origin) dan nama branch secara lengkap, kita membangun kebiasaan untuk selalu sadar dan yakin ke mana kode kita akan dikirim. Sehingga dapat mengurangi risiko kesalahan, seperti secara tidak sengaja melakukan push ke branch yang salah.</p><p><strong>2. Merge Request yang Jelas &amp; Informatif<br></strong>Setelah branch berhasil di-push, pekerjaan di terminal telah selesai. Langkah terakhir adalah membuat Merge Request (MR) di GitLab agar perubahan yang kerjakan bisa digabungkan ke branch utama.</p><p>MR yang baik mempercepat proses review dan membuatnya lebih efisien. Bukan sekadar notifikasi, melainkan ringkasan konteks yang memudahkan reviewer menilai perubahan dengan jelas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/702/1*1QcGHQ6CPHiPIWKnO70LTg.png" /></figure><p>Mari kita bedah setiap bagian penting dari <em>Merge Request</em> yang kita buat di atas:</p><ul><li>Judul (Title): Judul MR dibuat ringkas dan informatif, mengikuti format yang sama dengan pesan commit (tipe: deskripsi) sehingga bisa langsung dipahami inti dari MR tersebut.</li><li>Deskripsi (Description): Bagian untuk memberikan konteks. Di dalamnya, ada beberapa komponen kunci:<br><strong>Issue</strong>: Selalu tautkan MR ke issue yang relevan agar membantu melacak pekerjaan dari awal hingga akhir. <br><strong>What did you do</strong>: Jelaskan secara singkat perubahan apa yang dibuat dan mengapa.<br><strong>Screenshot / Video</strong>: Melampirkan screenshot atau bukti visual lainnya yang membuktikan bahwa perubahan sudah sesuai harapan.<br><strong>Note for Deployment</strong>: Memberikan catatan yang diperlukan jika perubahan membutuhkan pengaturan tambahan pada saat deploy.</li></ul><p>Kita telah melalui seluruh siklus kerja, dari persiapan branch hingga membuat Merge Request yang siap direview. Pada akhirnya, manajemen branch yang efektif bukanlah sekadar kumpulan aturan teknis, melainkan sebuah kebiasaan untuk berkomunikasi secara baik dengan tim. Alur kerja ini membuat riwayat proyek lebih jelas dan kolaborasi tim lebih efisien.</p><p>Semoga bermanfaat!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=87114c6a673b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dot-intern/manajemen-branch-git-yang-efektif-dari-task-baru-hingga-siap-di-review-87114c6a673b">Manajemen Branch Git yang Efektif: Dari Task Baru Hingga Siap di Review</a> was originally published in <a href="https://medium.com/dot-intern">DOT Intern</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>