3 poin oleh GN⁺ 4 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • Linear adalah alat produktivitas yang menangani pekerjaan manajemen issue dengan database di dalam browser dan sinkronisasi local-first, sehingga pembaruan issue tercermin di UI dalam hitungan milidetik
  • Database aktual yang dibaca UI berada di IndexedDB, dan perubahan diterapkan terlebih dahulu secara lokal lalu dikirim secara asinkron ke server, sementara delta didistribusikan kembali lewat WebSocket
  • Muat awal memakai strategi loading yang mengurangi waktu tunggu jaringan: pengiriman JavaScript dan CSS yang sedikit, pemecahan kode agresif, modulepreload, precache service worker, dan app shell inline
  • Mesin sinkronisasi menghidrasi data IndexedDB ke dalam pool objek MobX, menyimpan perubahan ke antrean transaksi, dan hanya merender ulang sel yang diperlukan dengan state observable per field
  • Kesan cepat yang dirasakan adalah hasil dari desain sistem yang juga mencakup input berpusat pada keyboard, command palette global, animasi ramah GPU, dan waktu transisi yang singkat

Database di dalam browser

  • Aplikasi web CRUD tradisional melewati permintaan HTTP setelah klik pengguna, kueri database di server, penerimaan respons, lalu repaint browser, sehingga selama ratusan milidetik muncul spinner, skeleton, atau UI yang terasa macet
  • Linear menempatkan database aktual yang dibaca UI di IndexedDB browser, menerapkan perubahan lebih dulu secara lokal lalu mengirimkannya secara asinkron ke server, dan server menyiarkan delta ke klien lain melalui WebSocket
  • Hambatan terbesar pada aplikasi web cepat adalah jaringan, dan transfer data antara klien dan server menimbulkan biaya ratusan milidetik
  • Alur inti Linear adalah membuat permintaan jaringan tidak terlihat oleh pengguna dan menghilangkan state loading sebisa mungkin
// Linear
issue.title = "Faster app launch";
issue.save();
  • issue.title = "Faster app launch" memperbarui penyimpanan data di memori, dan dalam kasus Linear menggunakan observable MobX
  • issue.save(); adalah tindakan saat mesin sinkronisasi memproses secara batch dan memasukkan transaksi ke antrean untuk di-flush ke server
  • UI dirender ulang secara sinkron berdasarkan perubahan memori lokal, dan sinkronisasi data berlangsung di latar belakang sehingga tidak memerlukan spinner
  • Tuomas mengatakan dalam konferensi tahun 2024 bahwa kode pertama yang ia tulis di Linear adalah mesin sinkronisasi, dan menyebutnya sebagai pendekatan yang tidak lazim di startup
  • Sebagian besar aplikasi tidak perlu membuat mesin sinkronisasi sendiri seperti Linear, dan kecepatan yang terasa sangat mirip bisa dicapai hanya dengan optimistic update dari TanStack Query dan SWR
  • Permintaan optimistis memberi dampak peningkatan besar dengan menghapus spinner yang tidak perlu, memperbarui state seketika, memverifikasi di latar belakang, dan melakukan rollback bila perlu
  • Responsivitas UI seharusnya tidak bergantung pada latensi jaringan, dan kecepatan yang dirasakan pengguna ditentukan oleh kecepatan respons antarmuka, bukan kecepatan respons server
  • Mengintip stack Linear

    • Linear dibangun di atas stack sederhana seperti React, TypeScript, MobX, Postgres, dan CDN
    • Frontend menggunakan React dan react-dom, MobX, TypeScript, Rolldown-Vite dan plugin-react-oxc, ProseMirror dan y-prosemirror, Radix UI primitives, Emotion dan StyleX, Comlink, idb, graphql-request, Sentry, serta Inter Variable
    • Backend menggunakan Node.js dan TypeScript, PostgreSQL di atas Cloud SQL, Redis Memorystore, turbopuffer, Kubernetes di GCP, dan Cloudflare Workers
    • Klien desktop berbasis Electron, sedangkan mobile diimplementasikan ulang sepenuhnya secara terpisah dengan Swift untuk iOS dan Kotlin
    • Situs marketing menggunakan Next.js, styled-components, dan inline SVG sprite
    • Linear tetap memakai client-side rendering, dan menjadi contoh bahwa CSR pun bisa terasa instan jika arsitektur dan desainnya tepat
    • Dengan mempertahankan seluruh aplikasi di sisi klien, mereka memperoleh model mental yang lebih sederhana yang mengurangi kompleksitas seperti pemisahan server-klien, apakah window bisa diakses, dan pengaturan cache header

Membuat pemuatan pertama terasa seketika

  • Dalam alat produktivitas, waktu yang dibutuhkan hingga benar-benar bisa mulai bekerja adalah detail yang penting
  • Pemuatan awal aplikasi client-side bisa melambat karena urutannya adalah permintaan index.html, permintaan JavaScript dan CSS, pemrosesan autentikasi, lalu permintaan API untuk menampilkan aplikasi
  • Alur bundler Linear: Parcel, Rollup, Vite, Rolldown

    • Kesan cepat secara instan dimulai dari build time, sebelum runtime, dan untuk pemuatan yang cepat penting untuk mengurangi jumlah JavaScript dan CSS yang dikirim
    • Linear menulis ulang pipeline build mereka dalam urutan Parcel → Rollup → Vite → Rolldown, dengan setiap perpindahan bertujuan mengurangi jumlah JavaScript·CSS dan meningkatkan pengalaman developer
    • Angka peningkatan menurut blog Linear
    • Kode yang dikirim berkurang 50%
    • Ukuran setelah kompresi berkurang 30%
    • Page load cold cache membaik 10~30%
    • Time-to-first-paint pada tampilan active-issues di Safari berkurang 59%
    • Penggunaan memori berkurang 70~80%
    • Sebagian besar peningkatan berasal dari kombinasi keputusan untuk hanya menargetkan browser modern, dead-code elimination yang lebih baik, dan code splitting yang agresif
    • Penghentian dukungan legacy membawa manfaat besar, termasuk penghapusan polyfill, transpile ES5, dan fallback nomodule
    • Bahkan setelah optimasi, Linear masih mengirim sekitar 21MB JavaScript yang telah diminify, tetapi mereka membaginya secara agresif menjadi ratusan chunk tingkat route untuk diambil hanya saat diperlukan
    • Poin utamanya bukan pilihan bundler tertentu, melainkan penghapusan browser legacy, peralihan ke ESM native, dan code splitting yang agresif
    • Langkah-langkah ini terakumulasi sehingga JavaScript first load Linear berkurang kira-kira setengah, dan waktu build turun bukan hanya satu digit, tetapi satu orde magnitudo
  • Preload setelah pemuatan awal

    • Saat JavaScript dipecah menjadi chunk kecil, muncul masalah pemuatan berantai seperti air terjun ketika tiap chunk mengimpor chunk lain
    • Linear mengatur agar sebelum JavaScript dieksekusi, browser bisa melihat seluruh daftar dan mulai melakukan permintaan secara paralel, sehingga ketika entry script mencapai import pertama, chunk terkait sudah ada di cache
    • Dengan menyamakan nilai modulepreload di <head> dan crossorigin pada entry script, browser tidak memperlakukan preload dan import sebagai resource terpisah, melainkan memakai ulang fetch yang sudah di-cache
    • Timeline cold load berubah dari air terjun berurutan menjadi satu batch paralel, sehingga pekerjaan jaringan masih ada tetapi dilakukan sekaligus
    • Pekerjaan ini dijalankan di latar belakang saat pengguna pertama kali mencapai halaman login, dan beberapa detik kemudian seluruh aplikasi sudah tersimpan di cache sehingga bisa disajikan seketika
    Iklan
  • Service worker untuk kecepatan lebih tinggi dan kemampuan offline

    • Chunk tingkat route untuk tampilan yang belum dikunjungi pengguna di-cache oleh service worker di latar belakang
    • Service worker memiliki precache manifest yang tertanam di source, dan sekitar 1.200 asset ter-hash mencakup chunk route, ikon, dan font
    • Strukturnya membuat seluruh aplikasi masuk ke cache dalam beberapa detik setelah mencapai layar login
    • Setelah itu, navigasi sepenuhnya melewati jaringan, dan service worker merespons langsung dari cachenya sendiri tanpa melalui HTTP cache
    • Jika digabung dengan engine sinkronisasi local-first dan data pengguna yang sudah tersimpan di IndexedDB, Linear dapat digunakan bahkan saat offline
    • Mendukung membaca issue, membuat issue baru, mengedit judul dan deskripsi, serta mengubah status
    • Semua pekerjaan diantrekan ke penyimpanan transaksi lokal, lalu di-flush saat koneksi kembali
    • modulepreload adalah mekanisme untuk mengambil hal yang dibutuhkan sekarang secara paralel agar browser tidak terhambat oleh serial import chain
    • Service worker adalah mekanisme untuk menyiapkan hal yang akan dibutuhkan berikutnya
    • Tahap pemuatan cepat Linear adalah menghapus sebanyak mungkin kode, membaginya menjadi potongan kecil, dan melakukan precache di latar belakang, dengan tujuan mempercepat atau sepenuhnya menghilangkan permintaan jaringan
  • Struktur vendor bundle

    • Setiap paket yang digunakan Linear dipecah menjadi chunk terpisah dan di-cache secara independen
    • vendor.js tradisional akan membuat cache seluruh grafik dependensi tidak valid bahkan jika hanya satu dependensi yang diperbarui
    • Pemecahan chunk Linear menciptakan caching vendor yang lebih terperinci alih-alih satu file besar tunggal, sehingga saat dependensi tertentu diperbarui hanya chunk itu yang tidak valid dan sisanya tetap ada di cache
  • Memuat file font besar

    • Pemuatan font yang salah dapat menyebabkan teks tidak terlihat sesaat, layout shift saat font sebenarnya menggantikan font fallback, dan fetch ganda akibat ketidakcocokan preload
    • Linear melakukan preload font Inter Variable di <head>, dan preconnect ke static.linear.app
    <link rel="preload"
          href="https://static.linear.app/fonts/InterVariable.woff2?v=4.1";
          as="font" type="font/woff2" crossorigin="anonymous">
    <link rel="preconnect" href="https://static.linear.app"; crossorigin>
    
    • Variable font menangani seluruh sumbu weight 100~900 dalam satu woff2 sehingga menghilangkan permintaan per weight
    • font-display: swap merender fallback stack segera, lalu menggantinya setelah Inter selesai dimuat
    • crossorigin="anonymous" pada tag preload adalah pengaturan kunci yang membuat browser memakai ulang resource yang sudah di-cache saat CSS kemudian merujuk font yang sama
    • Tanpa crossorigin, mode CORS antara preload dan referensi CSS menjadi berbeda sehingga browser mengambil ulang font tersebut
  • App shell inline

    • Linear menaruh CSS inline di dalam <head> yang cukup untuk menggambar status loading, sehingga app shell dapat ditampilkan tanpa permintaan stylesheet eksternal
    • JavaScript inline langsung menjalankan percabangan yang diperlukan untuk pengalaman awal
    • Mendeteksi Electron dan user agent Linear untuk menambahkan kelas electron
    • Jika localStorage.ApplicationStore tidak ada, tambahkan kelas logged-out
    • Memulihkan shell token seperti latar belakang sidebar, lebar sidebar, dan mode gelap dari localStorage.splashScreenConfig
    • Jika pengguna mengatur agar tautan dibuka di aplikasi desktop, lebar sidebar disesuaikan menjadi 8px
    • Sebelum bundle JavaScript pertama tiba dari jaringan, layar loading sudah memiliki tema, ukuran, dan posisi yang sesuai dengan status login
    • Cara tercepat untuk membuat pengguna merasa aplikasi sudah siap tepat setelah menekan Enter usai mengetik URL adalah dengan mengirimkan app shell dalam respons awal index.html
    Iklan
  • Render dulu, autentikasi belakangan

    • Alur autentikasi umum berjalan dalam urutan fetch HTML, load bundle, validasi sesi, fetch pengguna, fetch workspace, lalu render, dan pengguna mungkin harus menunggu 1~3 detik sebelum melihat sesuatu
    • Linear memperlakukan autentikasi dengan cara yang sama seperti penanganan perubahan, yaitu mengasumsikan jalur normal dan memverifikasi di latar belakang
    • Sebagian besar aplikasi CRUD menyimpan sesi sebenarnya di cookie HttpOnly, lalu menambahkan cookie terpisah yang bisa dibaca JavaScript atau permintaan /me agar frontend bisa mengetahui status login saat startup
    • Boot script inline Linear hanya memeriksa keberadaan localStorage.ApplicationStore, bukan sinyal autentikasi paralel
    if (localStorage.getItem("ApplicationStore") === null) {
      document.documentElement.classList.add("logged-out");
    }
    
    • Jika ApplicationStore ada, berarti pengguna pernah memakai Linear di browser ini dan data workspace sudah ada di IndexedDB
    • Jika nilainya tidak ada, tidak ada data untuk dirender, sehingga shell beralih ke layout logged-out dan alur login berlanjut
    • Token sesi sebenarnya ada di cookie, dan bundle tidak menilai status sesi sebelumnya
    • Jika salah satu dari WebSocket handshake, sync delta, atau panggilan HTTP menerima 401 karena sesi kedaluwarsa, client akan diarahkan ke login
    • Pola keseluruhannya adalah mempercayai data lokal untuk render instan, menempatkan server sebagai sumber kebenaran, lalu menyelaraskan keduanya secara asinkron

Mesin sinkronisasi

  • Kecepatan Linear berawal dari keputusan untuk memandang server bukan sebagai source of truth UI, melainkan sebagai target sinkronisasi
  • Kecepatan bukan hasil dari satu elemen tunggal, melainkan hasil dari tiga sumbu yang saling terkait
  • 1. Datanya sudah ada

    • Saat aplikasi dinyalakan, workspace tidak diambil dari server, melainkan dihidrasi dari IndexedDB ke dalam pool objek MobX di memori
    • Semua kueri UI pertama-tama mengarah ke pool objek, dan karena issue sudah ada di perangkat pengguna, tidak ada status “memuat issue”
    • Saat melakukan skalasi, Linear membagi data mesin sinkronisasi menjadi beberapa chunk dengan prinsip yang mirip bundel JavaScript
    • Dua tabel terberat, yaitu Issue dan Comment, tidak diambil sekaligus, melainkan di-lazy-hydrate saat diperlukan
    • Pendekatan ini adalah pemisahan kode pada level data, sehingga biaya awal mengikuti struktur workspace, bukan ukuran workspace
    • Workspace dengan 10.000 issue pun dapat boot hampir secepat workspace dengan 100 issue
    • Saat masuk ke proyek, issue sudah ada; saat memfilter berdasarkan assignee, indeksnya pun sudah terbangun
  • 2. Perubahan tidak menunggu jaringan

    • Saat status issue diubah, ada tiga hal yang terjadi hampir bersamaan
    • Perubahan diterapkan ke UI melalui pembaruan observable MobX
    • Perubahan dicatat ke antrean transaksi tahan lama di IndexedDB
    • Perubahan ditambahkan ke antrean pengiriman ke server
    • Pada titik ini, jaringan bahkan belum digunakan
    • Pengguna tidak perlu menunggu untuk melihat perubahannya sendiri, dan retry, rollback, serta reload across durability semuanya ditangani di latar belakang
    • Jika server menolak, observable akan dikembalikan dan terjadi flicker singkat, tetapi sebagian besar perubahan yang salah sudah tertangkap sebelum transaksi dibuat
    • Alur Linear dimulai dari perubahan lokal dan memperlakukan server bukan sebagai tahap izin, melainkan tahap konfirmasi
  • 3. Satu delta, satu sel

    • Saat server mengonfirmasi perubahan pengguna atau perubahan dari orang lain, sebuah JSON envelope kecil yang menunjukkan apa yang berubah akan dikirim kembali ke klien
    • Klien menerapkan perubahan dengan menuliskan nilainya ke observable MobX yang sesuai
    • Semua properti model di Linear masing-masing adalah observable, dan semua komponen yang membaca properti itu dibungkus dengan observer()
    • MobX dapat mengetahui dengan tepat komponen mana bergantung pada field mana
    • Perubahan satu field pada satu issue hanya akan me-render ulang komponen yang membaca field itu, bukan me-render ulang daftar induk atau seluruh sidebar
    • Pembaruan pada 50 issue berarti render ulang 50 sel, bukan render ulang seluruh daftar
    • Bahkan di workspace sibuk tempat 10 orang mengedit secara bersamaan, biaya menerima pembaruan meningkat sesuai item yang benar-benar berubah, bukan seluruh item yang ada di layar
    Iklan
  • Mengapa tiga hal ini saling terkait

    • Jika hanya ada database lokal tanpa penulisan optimistis, spinner tetap akan muncul saat menyimpan
    • Jika hanya ada penulisan optimistis tanpa observable yang terperinci, semua pembaruan tetap akan tersendat
    • Jika hanya ada observable yang terperinci tanpa database lokal, pengguna tetap harus menunggu pada pemuatan awal
    • Kecepatan Linear bukan sifat dari satu lapisan, melainkan sifat dari keseluruhan sistem
    • Bundler dan loader shell membuat first paint terasa cepat, dan mesin sinkronisasi menjaga rasa cepat itu tetap bertahan setelah pengguna mulai memakai aplikasi

Desain untuk kecepatan

  • Kecepatan adalah masalah rekayasa sekaligus masalah desain
  • Jika jalur aksi tercepat tetap membutuhkan mouse, tiga menu, dan klik, pengguna akan tetap membayar biaya langkah-langkah itu terlepas dari cepatnya mesin internal
  • Sumbu lain dari kecepatan Linear adalah integrasi keyboard sebagai alat utama untuk navigasi dan menyelesaikan pekerjaan
  • Setiap tugas umum punya shortcut, command palette bisa dibuka dengan satu penekanan tombol, dan menu klik kanan dibangun secara kustom
  • Setiap aksi punya shortcut

    • Satu karakter digunakan untuk mengedit issue yang sedang fokus, kombinasi dua huruf digunakan untuk navigasi, dan modifier digunakan untuk perilaku global
    • Sejak tahap awal Linear, shortcut adalah elemen fondasi, dan mesin sinkronisasi dirancang agar tindakan apa pun bisa dilakukan kapan saja
    • Shortcut terlihat di seluruh UI, dan shortcut yang paling sering dipakai adalah satu karakter
    • Agar tidak mengecualikan pemula, semua aksi juga bisa dilakukan dengan mouse
  • Command palette selalu berjarak satu tombol

    • ⌘ k membuka command palette yang dapat mencari hampir semua aksi di Linear
    • Target pencarian mencakup issue, proyek, label, perubahan status, navigasi, pembuatan issue, pengaturan, toggle tema, dan lain-lain
    • Command palette sangat cepat karena mencari di pool objek MobX lokal, bukan ke server
    • Seluruh aplikasi dapat diakses dari satu pane, dan navigasi, pembuatan issue, serta perubahan status semuanya dilakukan lewat pencarian
    • Command palette beradaptasi dengan konteks kerja saat ini, dan berfungsi dengan cara mengajarkan aksi inti serta shortcut untuk setiap view
    • Aplikasi yang cepat membutuhkan rekayasa dan desain yang sama-sama unggul; kecepatan rekayasa membuat satu interaksi menjadi cepat, sedangkan kecepatan desain memendekkan jalur menuju interaksi itu
    • Dalam alat yang dipakai sepanjang hari, perbedaan antara shortcut dan jalur mouse 2 detik akan terakumulasi pada setiap aksi

Animasi

  • Animasi yang buruk bisa membuang kembali milidetik yang sudah dihemat lewat optimasi initial load, update, dan kueri database pada tahap akhir
  • Elemen seperti animasi height selama 500ms bisa merusak upaya untuk membuat pengguna tidak perlu menunggu
  • Hanya ada beberapa properti yang layak dianimasikan

    • Perubahan properti di browser memiliki tiga lapisan biaya tergantung posisinya dalam rendering pipeline
    • Properti composited seperti transform dan opacity memindahkan pekerjaan ke GPU dan berjalan independen dari main thread
    • Properti pemicu paint seperti color, background-color, border-color, fill melewati layout tetapi memicu redraw piksel
    • Properti pemicu layout seperti width, height, top, left, margin, padding membuat posisi semua elemen setelahnya harus dihitung ulang, sehingga tidak seharusnya menjadi target animasi
    Iklan
    /* Cara Linear */
    .row:hover {
      background-color: var(--color-bg-hover);
      transition: background-color 0.12s;
    }
    .icon-arrow {
      transform: translateX(0);
      transition: transform 0.15s;
    }
    
    • Jika margin-left dianimasikan, layout semua row di bawah row yang di-hover akan dihitung ulang di setiap frame selama keseluruhan transisi 200ms
    • Dalam daftar issue yang panjang, perbedaan ini menjadi penentu antara tampilan mulus dan jank
    • Sebagian besar properti animasi di Linear adalah properti composited seperti transform dan opacity, dan sesekali memakai background-color serta border-color
  • Harus tahu kapan perlu menahan diri

    • Dalam alat yang dipakai setiap hari, animasi yang terlihat bagus di situs pemasaran bisa mengganggu pekerjaan
    • Sedikit hover delay di posisi yang salah pun bisa jadi sesuatu yang disadari pengguna
    • Banyak animasi di Linear efektif karena merujuk pada origin
    • Popover status melakukan scale out dari status pill, dan panel agent slide in dari toggle
    • Gerakan seperti ini bukan fade dekoratif, melainkan berperan secara spasial dengan memberi tahu dari mana elemen baru muncul
  • Jaga durasi tetap singkat dan instan

    --speed-highlightFadeIn: 0s;
    --speed-highlightFadeOut: .15s;
    --speed-quickTransition: .1s;
    --speed-regularTransition: .25s;
    --speed-slowTransition: .35s;
    
    • Banyak design system menetapkan durasi default yang lebih panjang dari yang diperlukan
    • Durasi standar Material adalah 200ms, dan spring iOS mendekati 350ms
    • Nilai default Linear berada di sisi yang lebih singkat dibanding praktik industri
    • Linear menggunakan timing asimetris untuk enter dan exit
    • Hover highlight, popover, dan panel agent muncul seketika saat dipanggil, lalu fade out selama 150ms saat ditutup
    • Jendela agent muncul seketika dan fade out mirip macOS

Cara Linear menjadi cepat

  • Performa Linear bukan hasil dari satu rahasia atau satu teknologi, melainkan akumulasi dari ratusan keputusan yang tepat
  • Banyak dari pendekatannya sebenarnya sederhana, dan merupakan hasil dari menetapkan arsitektur yang cocok untuk pengguna sejak awal lalu mempertahankannya, tanpa Next, TanStack, atau framework yang mencolok
  • Server tidak berperan sebagai source of truth untuk UI, melainkan sebagai sync target
  • Database berada di dalam browser, dan perubahan diterapkan lebih dulu secara lokal lalu diselaraskan di background
  • Initial load mengirim lebih sedikit kode dalam lebih banyak potongan, dan service worker melakukan precache sisanya saat pengguna berada di halaman login
  • Autentikasi mengasumsikan jalur normal berdasarkan state lokal lalu memverifikasinya belakangan
  • Sync engine melakukan hidrasi dari IndexedDB ke observable MobX per properti, sehingga pembaruan 50 issue diproses sebagai render ulang 50 sel, bukan render ulang seluruh daftar
  • Model input mengutamakan keyboard, dan semua tugas umum memiliki shortcut serta command palette global
  • Animasi tetap memakai properti yang ramah GPU, dan tidak menganimasikan properti pemicu layout
  • Bagian tersulitnya bukan implementasi itu sendiri, melainkan sikap untuk terus fokus pada kualitas detail selama bertahun-tahun ketika codebase matang, meluas, dan menghadapi batasan baru

1 komentar

 
GN⁺ 4 jam lalu
Komentar Hacker News
  • Jika ingin menghadirkan pengalaman seperti ini ke aplikasi, ada baiknya melihat Zero(https://zero.rocicorp.dev/)
    Demo langsung: https://gigabugs.rocicorp.dev/
    Daftar alternatif juga ada di sini: https://zero.rocicorp.dev/docs/when-to-use#alternatives
    Jika penasaran dengan cara kerjanya secara internal, dokumentasi desain Replicache juga layak dibaca: https://doc.replicache.dev/concepts/how-it-works
    Replicache adalah pendahulu Zero, dan protokol intinya masih bekerja dengan cara yang sama

    • Zero benar-benar layak direkomendasikan. Abstraksinya luar biasa dan ini perangkat lunak yang dibuat dengan sangat cermat
      Bukan cuma ada keuntungan performa yang jelas dari sinkronisasi data ke klien, saya juga terkejut melihat betapa sederhananya kode React menjadi. Dengan adanya mesin sinkronisasi, sebagian besar state klien menghilang, dan sebagian besar kode komponen bisa dipikirkan secara sinkron
    • Saya sudah memakai Zero cukup lama. Awalnya saya ingin membangun mesin sinkronisasi ala Linear secara internal, lalu menemukan Zero
      Ini mungkin pilihan yang paling mendekati tanpa harus membentuk tim khusus
    • Sebagai pengguna Zero, ini adalah alat yang tepat ketika Anda menginginkan pengalaman pengguna di mana UI langsung diperbarui dalam hitungan beberapa milidetik saat database berubah
  • Saya selalu mendengar bahwa Linear itu cepat, tetapi setelah benar-benar memakainya setiap hari, antusiasme saya memudar. Pencariannya cukup lambat, UI-nya sering terasa berat, dan meski terlihat bagus, “Pulse” terasa seperti banjir kebisingan bahkan pada skala kecil
    Sulit menemukan hal yang dibutuhkan sehingga akhirnya saya memasukkan semuanya ke favorit. Trello versi awal jauh lebih unggul sebagai pengalaman pelacakan proyek

    • Saya benar-benar benci saat harus membuka tiket ketika rapat atau huddle, lalu cuma menatap canggung sambil menunggu proses entah loading atau caching yang lamanya tidak masuk akal
  • Tahun lalu seseorang melakukan reverse engineering pada mesin sinkronisasi Linear lalu mengunggahnya ke GitHub lengkap dengan penjelasan yang keren
    https://github.com/wzhudev/reverse-linear-sync-engine/blob/m...

  • Aplikasi web sinkronisasi local-first seperti ini memang sangat menarik dan bisa sangat berguna, tetapi menurut saya premisnya agak keliru
    Premisnya seperti, “Di Linear, memperbarui issue cukup butuh beberapa milidetik. Aplikasi CRUD tradisional membutuhkan sekitar 300ms untuk tugas yang sama”, dan “Semua data yang bolak-balik antara klien dan server harus membayar biaya ratusan milidetik”
    Memang kita tidak bisa menyelesaikan masalah membesarnya waktu pulang-pergi antara klien HTTP dan server karena kecepatan cahaya, tetapi backend tetap bisa ditempatkan dekat pengguna dan dibuat cepat
    Misalnya, sangat mungkin menjalankan backend aplikasi web yang berada sekitar 10ms round-trip dari sebagian besar pengguna, dan backend itu juga merender respons dalam sekitar 10ms. Artinya, aplikasi CRUD tradisional pun bisa dibuat menyelesaikan tugas yang sama bukan dalam 300ms melainkan sekitar 30ms

    • Saya juga merasa aneh melihat 300ms disebut cepat, karena sejauh yang saya ingat TTFB 30ms sudah lama menjadi target
      Mungkin ada alasan yang sah mengapa Linear membutuhkan waktu lebih lama di backend sehingga perlu bantuan frontend, tetapi itu tidak bisa digeneralisasi. Setiap potongan JavaScript juga punya biayanya sendiri
    • Pernyataan bahwa “mungkin menjalankan backend aplikasi web yang berada sekitar 10ms round-trip dari sebagian besar pengguna” terdengar aneh. Dari us-east-1, satu-satunya region AWS yang di bawah 10ms pada praktiknya hanyalah us-east-2: https://www.cloudping.co/
      us-west-1 berjarak 60ms, eu-centra-1 100ms, dan Asia 200ms. Itu pun lalu lintas antar data center, sedangkan latensi nyata di internet publik hingga koneksi rumahan jauh lebih buruk
      Database harus berada tepat di satu region. Di mana pun diletakkan, mayoritas pengguna di bumi akan berada lebih dari 100ms dari sana
      Alasan endpoint tidak banyak membantu adalah karena endpoint tetap harus berkomunikasi dengan database untuk membaca dan menulis data. Begitu Anda mencoba mereplikasi data agar dekat dengan pengguna, pada akhirnya Anda akan memiliki database sinkronisasi local-first
      Entah membangunnya sendiri atau memakai produk jadi, database replika itu akan menghadapi semua masalah yang sama seperti sinkronisasi sisi klien, dan tetap menyisakan latensi jaringan yang signifikan. Fisika tidak bisa dihindari, jadi untuk sebagian besar pengguna pilihannya hanya memberi commit 0,25 detik atau final consistency, yaitu sinkronisasi
    • Itu hanya mungkin jika para pengguna berada cukup berdekatan satu sama lain, atau kalau, sayangnya seperti yang sering terjadi, yang penting cuma pengguna di AS cepat dan sisanya tidak terlalu dipedulikan
      Tentu saja Anda bisa menaruh “backend perantara” di tempat seperti jaringan edge CDN global, tetapi pada titik itu Anda akan membayar biaya kompleksitas yang sama seperti pendekatan ini yang menaruh “backend perantara” di klien
    • Anda bisa menaruh backend perantara di klien, menulis perubahan yang belum diproses ke penyimpanan lokal, lalu membiarkan background worker mengirimkannya ke backend dengan retry yang diperlukan
      Dalam skenario terburuk, background worker cukup mengirim pesan kegagalan pembaruan lalu UI thread menerimanya dan menampilkannya. Jalur suksesnya tetap secepat kilat
    • Apakah ini tetap mungkin ketika semua backend edge itu harus berbagi database yang sama?
  • Sulit membangun database eventual consistency, dan itu mungkin cocok untuk kasus penggunaan Linear, tetapi jadi masalah ketika saya tidak tahu apakah pembaruan saya sudah sampai ke server, yaitu ke tim
    Di proyek-proyek lain yang pernah saya ikuti sebelumnya, jeda sinkronisasi menimbulkan tak terhitung banyaknya masalah, jadi saya selalu memilih solusi sinkron. Fitur-fitur mewah hanya saya keluarkan saat benar-benar perlu; saya lebih memilih mengoptimalkan server secepat mungkin lalu membiarkan pengguna menanggung latensi jaringan

    • Saya pernah mengalami inkonsistensi di Linear, tapi karena Jira itu tempat sampah, ya mau bagaimana lagi
  • Perusahaan saya memakai Linear. Mungkin saya termasuk minoritas, tapi pengalaman pengguna-nya benar-benar berat. Bahkan sulit menyebutnya cepat
    Halamannya sendiri secara teknis lumayan cepat dimuat, tetapi sekitar setengah dari waktu saya melihat angka-angka di halaman berubah tanpa ada indikasi visual bahwa pemuatan data masih berlangsung

    • Masalah yang sering saya alami di Linear adalah penulisan berulang kadang saling menimpa. Saya mengetik, berhenti sebentar untuk berpikir, lalu mengetik lagi, dan Linear mengembalikan data ke keadaan setelah input pertama
      Sebegitu buruknya sampai di Linear saya hanya membuat issue dengan deskripsi satu kalimat, lalu untuk isi detailnya saya pindah ke GitHub. Hanya itu yang benar-benar dilakukan Linear dengan baik dan cepat
    • Linear telah berubah menjadi hal yang dulu ingin dihapusnya, yaitu alat yang kompleks
      Sayangnya, untuk bertahan hidup dan naik ke pasar yang lebih atas, praktis memang hanya itu jalannya
    • Saya tidak memakai Chrome, jadi saya tidak tahu apakah itu pengaruhnya, tetapi halaman Linear sering macet atau butuh beberapa detik untuk muat pertama kali
      Saya tidak akan memakai kata “cepat”. Kalau memuatnya saja butuh 30 detik, pengurangan pembaruan issue dari 300ms menjadi “beberapa” milidetik tidak terlalu penting
    • Di tempat kerja lama, kami beralih dari Linear ke Jira karena UX-nya aneh. Ada banyak ikon yang maknanya sulit dipahami, sulit ditemukan, dan hampir tidak ada penanda mengapa isi halaman berubah
    • Benar-benar mengerikan. Saya harus bertanya ke rekan kerja bagaimana cara menambahkan tanggal jatuh tempo ke sebuah item, dan ternyata tersembunyi di panel navigasi
      Memang lebih baik daripada Jira, tapi itu standar yang sangat rendah
  • Keren. Mungkin saya bisa memasukkan hal serupa ke game browser dan engine yang sedang saya kembangkan, sehingga setelah pemuatan pertama status loading bisa dihilangkan sepenuhnya. Punya saya adalah struktur aset statis sepenuhnya di sisi klien tanpa server
    Saya sudah terobsesi dengan performa game ini. Sebelum akhir pekan lalu, saya kesulitan mempertahankan 120fps di M1 MacBook Pro sambil mensimulasikan 128 pemain secara bersamaan, dengan pathfinding, logika strategi berat, dan rendering semuanya diproses di dalam viewport, dengan frame drop yang sangat sesekali terjadi dan waktu frame sekitar 4ms
    Sepanjang akhir pekan saya kerja keras pada performa, dan sekarang saya bisa mensimulasikan 2048 pemain secara bersamaan dengan waktu frame di bawah satu milidetik. Itu termasuk rendering, semua logika, dan generasi prosedural
    Saya juga menerapkan CPU throttling 11,2x untuk mensimulasikan perangkat mobile kelas bawah, dan bahkan saat itu tetap mendapat 60fps stabil pada 256–512 pemain simultan dengan waktu frame sekitar 5ms. Bottleneck utama saya sekarang adalah sedikit masalah logika dan waktu start/boot yang perlu ditingkatkan di perangkat low-end, dan sepertinya ada hal yang bisa dipelajari dari Linear

  • Saya justru merasa Linear sebenarnya cukup lambat. Pernah ada minggu-minggu ketika setelah membiarkan tab tetap terbuka cukup lama, CPU berjalan di 100%

    • Di Firefox sepertinya juga memakai banyak memori. Saya tidak bisa membiarkan lebih dari beberapa tab Linear terbuka sekaligus
  • Menarik juga. Sejujurnya saya tidak pernah menganggap Linear itu “cepat”. Seperti kebanyakan aplikasi web, tetap terasa ada latensi, tetapi dibandingkan JIRA tentu secepat kilat
    Linear sendiri luar biasa, dan setelah siksaan JIRA, rasanya benar-benar menyegarkan. Untuk bicara tentang route optimistis dan “cepat”, bukankah seharusnya kita mulai dari Gmail dulu

  • Jawaban untuk kecepatan adalah prefetching. Pada dasarnya ini berarti mengunduh database klien saat inisialisasi dan memiliki strategi invalidasi cache
    Saya membuat starfx untuk menangani sisi sinkronisasi data dari paradigma ini: https://starfx.bower.sh/learn#data-loading-strategy-stale-wh...