Mengapa GUI database tidak boleh menulis secara langsung
Setiap GUI database punya momen di mana sebuah klik berubah menjadi penulisan terhadap produksi. Kami memutuskan momen itu seharusnya tidak ada. Di DynoTable, tidak ada satu pun yang Anda lakukan — bukan penyuntingan item, bukan penghapusan massal, bukan perubahan dari — yang menyentuh DynamoDB secara langsung. Setiap penulisan mendarat di sebuah sebagai diff per-atribut yang dapat ditinjau, dan hanya dikirim ketika seorang manusia melakukan commit.
Model mental yang kami tuju adalah git: sebuah staging area untuk ditinjau, dan
sebuah commit yang berperilaku seperti git push --force-with-lease — ia hanya
berhasil jika remote masih tampak sebagaimana terakhir kali Anda melihatnya.
Postingan ini adalah engineering di baliknya: bug lintas-tab yang membentuk ulang
seluruh desain, refactor yang keluaran utamanya adalah kode yang dihapus, dan
mengapa kedatangan agen AI mengubah kenyamanan UX menjadi dinding pengaman penahan
beban.
Penulisan adalah diff
Suntingan menumpuk di sebuah stage lokal berbasis SQLite dan ditampilkan sebagai kartu diff — nilai lama, nilai baru, per atribut — di panel samping. Baris diwarnai di grid sehingga state yang di-stage terlihat dari datanya, bukan hanya dari panel. Commit mengirim set yang di-stage sebagai DynamoDB, dipotong menjadi bagian-bagian sesuai batas layanan (100 item per transaksi, dengan anggaran byte per operasi dan per permintaan), secara berurutan, dengan timeout 30 detik per bagian.

Dokumentasi staging membahas alur kerja sehari-hari. Yang tidak mereka bahas adalah dikunci berdasarkan apa stage itu — yang ternyata menjadi keputusan paling berkonsekuensi dalam sistem.
Dikunci ke tabel, bukan ke tab
Versi pertama membatasi setiap stage ke tab yang membuatnya. Desain itu menghasilkan bug nyata: tool staging milik AI menurunkan targetnya dari tab aktif, dan penjaganya melewati tab yang bukan tampilan tabel. Jadi jika Anda meminta asisten memperbaiki sebuah baris ketika sebuah tab SQL Workbench sedang difokuskan, suntingan itu di-stage ke stage milik Workbench — dan chip "tampilkan perubahan yang di-stage" membuka tab yang salah.
Lebih buruk lagi, commit lock juga bersifat per-tab. Dua tab yang menampilkan tabel yang sama memegang dua lock independen — artinya keduanya bisa melakukan commit baris yang sama yang di-stage ke DynamoDB nyata secara bersamaan. Sebuah race double-commit terhadap produksi, terpasang di dalam model data.
Perbaikannya adalah mengunci ulang semuanya berdasarkan identitas tabel —
{profile, region, tableName} — alih-alih berdasarkan tab:
- Satu stage per tabel, terlihat dari setiap tampilannya, bertahan melewati penutupan dan pembukaan ulang tab. AI bisa melakukan stage tanpa ada tab tabel yang terbuka sama sekali.
- Satu commit lock per tabel. Race double-commit itu bukan "ditangani" — ia tidak dapat direpresentasikan.
- Kelas bug salah-tab hilang secara konstruksi: tidak ada tab dalam key-nya.
Dan pengunci-ulangan itu menghapus lebih banyak kode daripada yang ditambahkannya. Sapuan startup yang meng-garbage-collect stage untuk tab yang tertutup? Seorang peninjau menandainya sebagai penghancur-fitur di bawah model baru — stage tidak lagi mati bersama tab — jadi ia dihapus sepenuhnya. Discard-on-tab-close dan dialog konfirmasinya: dihapus. Satu-satunya orphan sejati yang tersisa adalah profil koneksi yang dihapus, yang di-cascade secara eksplisit.
Migrasi itu sendiri adalah yang pertama di repo yang memutasi baris: pembangunan-
ulang tabel SQLite yang ditulis tangan yang mendeduplikasi baris per-tab lama dengan
window ROW_NUMBER() OVER (PARTITION BY …) (suntingan terbaru menang) dan mengisi
ulang key baru dengan pemisah char(0), byte-identik dengan pembangun key milik
runtime. Migrasi yang memutasi baris mendapatkan test harness seed-lalu-migrasi
mereka sendiri hari itu.
--force-with-lease, untuk DynamoDB
UI tinjauan tidak berguna jika yang Anda tinjau bukanlah yang ditulis. Antara staging dan committing, orang lain mungkin telah mengubah baris itu. Jadi setiap operasi commit membawa yang menyematkan penulisan ke snapshot persis yang Anda tinjau — , atribut demi atribut:
- Sebuah create menyatakan bahwa item belum ada.
- Sebuah update menyatakan bahwa setiap atribut yang Anda ubah masih menyimpan nilai yang Anda lihat ketika Anda melakukan stage.
- Bahkan sebuah removal menyatakan bahwa atribut masih sama dengan nilai lamanya —
jika rekan tim mengubah
notedari"old"menjadi"new"selagi penghapusan Anda atasnotemasih ter-stage, commit tidak boleh diam-diam menghapus suntingan mereka.
Ketika sebuah condition gagal, DynamoDB membatalkan seluruh transaksi dan memberi tahu kami item mana yang menyimpang. Baris itu mendapat banner konflik — rebase terhadap nilai live atau batalkan — alih-alih penimpaan diam-diam ke arah mana pun.
Dan pada kegagalan bagian mana pun, committer berhenti. Bagian yang telah di-commit tetap ter-commit, bagian yang gagal di-rollback secara atomik, tidak ada lagi yang dicoba. Kami sengaja menolak kelanjutan best-effort: sebuah tool tinjauan yang terus menulis melewati sebuah konflik berarti menerapkan set perubahan yang tidak ditinjau siapa pun.
Refactor yang menghapus sebuah subsistem
Commit berjalan lama: aplikasi mendaftarkan masing-masing dalam sebuah replay
registry sehingga reload renderer — atau jendela kedua — bisa terhubung kembali dan
menyaksikannya selesai. Seiring waktu, tiga jalur kode berbeda masing-masing memasang
dua observer per commit, dan sebuah mekanisme arbitrase utuh tumbuh di sekitar satu
pertanyaan: observer mana yang boleh melepaskan commit lock? Ia punya namanya sendiri
yang menyeramkan (ownsLockOnBeforeRegistration), sebuah blok komentar yang
menjelaskan race pengurutan IPC, dan sebuah toast tracker sepanjang 230 baris.
Perbaikannya adalah satu pemilik: sebuah Commit Session yang terpasang tepat sekali per commit, memiliki lock secara eksklusif, memproyeksikan progres ke dalam store, dan menjamin panggilan start-nya terselesaikan di setiap jalur keluar — tak pernah menggantung, tak pernah menolak. Mesin arbitrase dan tracker itu tidak dipindahkan; mereka dihapus. Benchmark memori yang menggempur panel staging turun sekitar sepertiga heap-nya. Komentar code review terbaik yang kami dapat bulan itu: "PR ini kebanyakan merah."
Dua perilaku muncul dari model kepemilikan itu yang kami anggap syarat dasar bagi tool apa pun yang menulis ke produksi: sebuah commit yang sedang berjalan bertahan melewati reload renderer (session terhubung kembali dan menyelesaikan), dan ia bahkan bertahan ketika lisensi Anda beralih ke read-only di tengah commit — commit baru diblokir, tetapi penulisan yang sudah berjalan disaksikan sampai selesai, tak pernah ditinggalkan setengah-diterapkan.
Mengapa format wire sengaja dibuat jelek
Nilai yang di-stage disimpan sebagai amplop bertipe DynamoDB mentah —
{N: "42"}, {S: "42"} — bukan sebagai JSON ter-unmarshal yang ramah. Dua alasan.
Diff harus sadar-tipe: {N: "1"} dan {S: "1"} adalah nilai yang berbeda, dan
hash primary key yang dibangun atas nilai ter-unmarshal akan menabrakkan keduanya.
Dan round-trip harus lossless: pembungkus angka ter-unmarshal milik SDK tidak
bertahan melewati serialisasi JSON ke dalam SQLite dan kembali, dan deep-equality
terhadap masukan pengguna rusak. Jalur commit menggunakan client mentah dengan alasan
yang sama — apa yang Anda tinjau adalah byte-demi-byte apa yang dikondisikan dan
ditulis.
Lalu para agen datang
Staging mendahului fitur AI kami, tetapi ia adalah alasan mereka bisa dirilis sama sekali. Tool tulis milik asisten melakukan stage; deskripsinya sendiri memberi tahu model — verbatim:
The user reviews + commits from the staging panel —
this tool never writes to DynamoDB directly.Tidak ada tool commit. Bukan bergerbang-izin, bukan teraudit — tidak ada. Server MCP mengekspos batas yang sama secara struktural: scope consent tengahnya benar-benar bernama "read + stage". Sebuah agen yang pada scope itu bisa mengusulkan sampah, dan radius ledakannya adalah sebuah kartu diff yang dibaca manusia. Dan karena commit membawa optimistic lock per-atribut, bahkan suntingan agen yang basi tak bisa diam-diam menimpa suntingan manusia yang bersamaan — ia muncul sebagai konflik seperti segala hal lainnya.
Kami telah menulis tentang membuat query agen dapat diandalkan; staging adalah paruh lainnya — membuat penulisan-nya membosankan.
Apa yang harus dituntut dari tool apa pun yang menulis ke database Anda
- Sebuah langkah tinjauan antara niat dan penulisan — diff, bukan dialog konfirmasi.
- Konkurensi optimistik pada snapshot yang ditinjau, termasuk penghapusan dan removal — tak pernah last-writer-wins.
- Batch atomik dengan halt-on-failure, bukan kelanjutan best-effort melewati sebuah konflik.
- Sebuah jalur tulis yang bertahan melewati crash atau reload tanpa meninggalkan set yang setengah-diterapkan.
- Untuk AI: staging sebagai primitif tulis satu-satunya yang dimiliki sebuah agen — kapabilitas yang absen mengalahkan yang dijaga.
Dokumentasi staging menunjukkan alur kerjanya, dan menyunting data DynamoDB membahas dasar-dasar yang dilindunginya. Atau unduh DynoTable, stage beberapa suntingan dengan ⌘S, dan saksikan sebuah perubahan massal menjadi sesuatu yang benar-benar bisa Anda baca sebelum terjadi.


