Berpikir seperti Senior Flutter Engineer di Era AI
Ada rahasia kotor di komunitas Flutter: ketika kita bilang "systems design," kebanyakan engineer langsung menggambar diagram load balancer dan message queue. Dan ya, saya paham — itu yang diajarkan buku-buku persiapan interview. Tapi begitu kamu diminta mendesain sistem *frontend* — widget tree, alur state, data layer dari sebuah aplikasi Flutter — ruangan langsung hening.
Ini masalahnya: frontend systems design itu nyata, itu sulit, dan justru di sinilah tools AI bakal bikin kamu jauh lebih produktif — atau berbahaya kalau asal-asalan. Nggak ada titik tengahnya.
Setahun terakhir ini saya habiskan untuk mengamati AI coding agents bikin aplikasi Flutter dengan kecepatan yang menakutkan. Sebagian kodenya bagus banget. Sebagian besarnya adalah tumpukan utang teknis yang disatukan oleh `StatefulWidget` yang menyentuh tiga API berbeda. Itu bukan masalah tooling. Itu masalah cara berpikir.
Mari kita perbaiki.
Apa Arti "Systems Design" Sebenarnya untuk Flutter
Ketika engineer senior mendengar "desain aplikasi chat," mereka nggak mulai dari `ListView.builder`. Mereka mulai dari batasan.
Backend engineer berpikir dalam service, database, dan queue. Flutter engineer perlu berpikir dalam:
- **Dekomposisi widget** — kapan widget 600 baris menjadi folder berisi widget-widget yang fokus?
- **Kepemilikan state** — state apa yang hidup di widget tree, apa yang hidup di controller, apa yang hidup di repository?
- **Alur data** — bagaimana arah satu arah data dari jaringan ke piksel?
- **Penanganan kegagalan** — di mana retry, fallback, dan error state berada?
Senior Flutter engineer melihat aplikasi seperti bawang. Lapisan luar adalah widget tree (presentasi). Lapisan tengah adalah manajemen state (logika aplikasi). Intinya adalah akses data (repository, service, penyimpanan lokal). Tugas systems design adalah menjaga lapisan-lapisan ini agar tidak meleleh satu sama lain.
Aturan yang Menyelamatkan Sanitasimu: Widget Tidak Boleh Tahu Business Logic
Ini aturan yang saya berikan ke setiap mentee: **widget kamu tidak boleh tahu business logic kamu, dan business logic kamu tidak boleh tahu widget kamu.**
Kalau `TextFormField` memanggil repository secara langsung, kamu sudah melanggar batasan. Kalau repository kamu tahu apa itu `TextEditingController`, kamu melanggar dari arah sebaliknya. Pemisahan inilah yang membuat kode bisa diuji, bisa diganti, dan — yang penting di era AI — memungkinkan agen mengedit kode tanpa meledakkan seluruh aplikasimu.
Bagaimana AI Mengubah Permainan
Tools AI seperti GitHub Copilot, Claude Code, dan Cursor sudah mengubah secara fundamental cara kode ditulis. Saya bisa membuat aplikasi CRUD yang lengkap dalam satu sore. Itu bukan hiperbola; itu Kamis.
Tapi inilah yang saya pelajari setelah puluhan proyek Flutter berbantuan AI: **agen itu hebat menulis kode, tetapi buruk mengambil keputusan arsitektural.**
Beri agen sebuah folder dengan widget yang belum lengkap dan instruksi yang jelas, dia akan menyelesaikannya dengan sempurna. Beri dia prompt yang ambigu seperti "buat aplikasi ini lebih baik," dan dia akan diam-diam membuat empat puluh keputusan yang tidak berhubungan — sebagian di antaranya salah secara fatal.
Inilah kenapa agen OpenClaw yang meretas sistem reservasi gym bisa viral. Bukan karena kodenya cerdas, tetapi karena agen itu diberi *tujuan* tanpa *batasan*. Pola yang sama saya lihat di banyak codebase Flutter ketika agen dilepas tanpa pagar arsitektural.
Skenario Satu: Kekacauan Buatan AI
Bulan lalu, seorang developer yang saya mentor membuka proyeknya dan menunjukkan apa yang dihasilkan agen. Tugasnya sederhana: "Tambahkan dukungan offline ke layar pesanan."
Yang dilakukan agen:
- Menambahkan `path_provider` dan `sqflite` ke `pubspec.yaml`
- Membuat database SQLite di dalam `initState` sebuah widget
- Membungkus HTTP client pihak ketiga dalam class custom tanpa interface
- Menyimpan seluruh respons JSON dalam static variable global
Secara teknis, itu "berhasil" — di happy path. Tapi koneksi database-nya bocor, cache-nya tidak punya strategi invalidasi, dan UI membeku di perangkat lambat karena operasi SQLite berjalan di main isolate.
Solusinya bukan prompt yang lebih baik. Solusinya adalah **systems design yang lebih baik** — memutuskan *sebelum* agen menulis kode bahwa persistensi akan tinggal di balik repository abstrak, memakai penyimpanan yang aman di compile-time seperti [drift](https://drift.simonbinder.eu), dan mengekspos stream state yang UI tinggal amati.
Pola Systems Design yang Wajib Dikuasai Setiap Flutter Engineer
Ini playbook yang saya pakai ketika diminta mendesain fitur Flutter — baik oleh manusia, agen, atau kombinasi keduanya.
1. Kue Tiga Lapis (Presentasi / Aplikasi / Data)
- **Lapisan presentasi**: widget, animasi, routing. Lapisan ini hanya mengonsumsi state dan memancarkan intent.
- **Lapisan aplikasi**: manajemen state (Riverpod providers, Bloc cubits, ChangeNotifiers). Lapisan ini mengubah intent menjadi perubahan state.
- **Lapisan data**: repository, service, database lokal, network client. Lapisan inilah yang berurusan dengan dunia luar.
Setiap lapisan hanya berbicara dengan lapisan tepat di bawahnya. Widget tidak pernah menyentuh `Dio`. Repository tidak pernah mengembalikan `BuildContext`.
Kalau kamu bertanya "validasi form taruhnya di mana?" atau "apakah API key harus masuk ke Provider?" — jawabannya selalu "di lapisan yang memiliki urusan itu."
2. Manajemen State sebagai Alur Satu Arah
Saya tidak peduli library manajemen state mana yang kamu pilih — [Riverpod](https://riverpod.dev), [Bloc](https://bloclibrary.dev), atau vanilla `InheritedWidget` sekalipun — selama alurnya satu arah:
```
Stream of State -> Widgets Render -> User Intents -> State Mutations -> Repeat
```
Begitu ada two-way binding, di situ ada bug. Begitu widget mengubah state secara langsung, di situ ada bug yang tidak bisa kamu reproduksi. Begitu ada `static var` global yang menyimpan data pengguna, di situ ada security review di masa depanmu.
3. Error Boundaries dan Graceful Degradation
Engineer senior mendesain untuk kegagalan terlebih dahulu. Setiap layar menjawab pertanyaan-pertanyaan ini:
- Apa yang terjadi kalau API mati?
- Apa yang terjadi kalau pengguna tidak punya internet?
- Apa yang terjadi kalau bentuk JSON berubah?
- Apa yang terjadi kalau data null?
Ini bukan soal membungkus semuanya dengan try-catch. Ini tentang mendesain *tipe state* yang mencakup `loading`, `loaded`, `error`, dan `empty` — lalu memastikan setiap widget tahu cara merender keempatnya. Saya jamin ini lebih berharga daripada package obscure mana pun.
Pendekatan "Spec First" untuk Flutter Berbantuan AI
Saya bilang tadi agen AI melenceng tanpa batasan. Solusi yang saya temukan dan konsisten berhasil adalah **spec-driven development**. Tulis spec *sebelum* agen mulai coding.
Ini alur kerjanya:
1. **Tulis Architecture Decision Record (ADR) satu halaman** untuk fitur tersebut. Jaga di bawah 500 kata. Sertakan struktur widget tree, hook manajemen state, dan kontrak data.
2. **Tentukan batasan file**. Sebutkan folder-folder: `features/checkout/widgets/`, `features/checkout/logic/`, `features/checkout/data/`. Beri agen jalur file yang eksplisit.
3. **Instruksikan agen untuk bertanya soal keputusan arsitektural, bukan membuatnya sendiri**. Satu kalimat ini mencegah 90% kisah horor arsitektur yang digerakkan AI.
4. **Tinjau kode agen terhadap ADR sebelum kamu menjalankannya**. Code review tetap alat uji terbaikmu.
Ini bukan birokrasi. Ini memastikan 500 baris kode agen mendarat di tempat yang benar. Karena alternatifnya adalah refactoring — dan merefactor kode buatan AI lebih buruk daripada merefactor kode manusia, karena *tidak ada* yang menulisnya dengan niat.
Skenario Dua: Streaming Respons AI (Ya, Kamu Sedang Membangun Fitur AI)
Kamu sedang membangun fitur chat AI. Agen bisa membuat layar streaming respons dengan animasi yang indah dalam hitungan menit. Tapi mundur selangkah dan berpikir di level sistem:
- Di mana riwayat percakapan tinggal? Memory, penyimpanan lokal, server?
- Apa yang terjadi ketika stream HTTP putus di tengah respons?
- Bagaimana cara rate-limit dan menangani 429?
- Bagaimana menangani konten yang tidak aman atau halusinasi di UI?
Engineer senior mendesain state streaming (`StreamBuilder`, state berbasis buffer, logika retry) sebelum menulis satu widget pun. Agen bisa mengisi sisanya.
Skenario Tiga: Aplikasi To-Do Offline-First
Buat aplikasi di mana pengguna mengelola tugas di pesawat. Pertanyaan systems design-nya: apakah kamu mengoptimalkan agar data tidak pernah hilang, atau untuk kecepatan?
Dengan database lokal berbasis `drift`, repository yang sinkron dengan REST API, dan connectivity provider yang melacak status jaringan, desainnya tetap bersih. Agen yang menulis pipa-pipanya; senior engineer yang mendesain *bentuk* pipa itu.
Apa yang Dibuat AI Menjadi "Tidak Perlu" (Dan Yang Tidak)
Ada gelombang opini yang mengklaim AI akan menghilangkan kebutuhan developer manusia. Sisi optimisnya: AI memberi kreator lebih banyak otonomi. Sisi pesimisnya: AI mengekspos celah yang selama ini disembunyikan otonomi. Andy Budd menulis tentang ini untuk desain digital, dan itu persis berlaku untuk Flutter: AI menghilangkan *gesekan* menulis kode, tetapi tidak menghilangkan *tanggung jawab* mengetahui apa yang harus ditulis.
AI bisa:
- Menghasilkan kode widget boilerplate lebih cepat daripada kamu mengetiknya
- Menerjemahkan mockup desain menjadi widget tree dalam hitungan detik
- Menulis unit test untuk repository kamu
AI tidak bisa:
- Memutuskan di mana state tinggal
- Memutuskan trade-off antara kesegaran cache dan biaya API
- Memutuskan seperti apa loading skeleton ketika API lambat
Itu adalah keputusan desain. Itu *tugasmu* — dan itu akan tetap menjadi tugasmu sepuluh tahun dari sekarang.
Checklist Singkat untuk Systems Design Flutter Berikutnya
Ketika seseorang memintamu mendesain aplikasi Flutter — di interview, di dokumen desain, atau sekadar di kepalamu — jalankan checklist ini:
1. **Apa atom fungsionalnya?** Pecah aplikasi menjadi fitur-fitur. Setiap fitur harus bisa dibangun, diuji, dan dihapus secara independen.
2. **Apa atom statenya?** Untuk setiap fitur, identifikasi: state mana yang lokal, mana yang shared, dan mana yang dipersist?
3. **Bagaimana data masuk dan keluar?** Setiap sumber data mendapat repository. Setiap repository mengembalikan objek bertipe, bukan JSON mentah.
4. **Apa yang terjadi ketika rusak?** Failure state untuk setiap panggilan async. Loading state untuk setiap stream.
5. **Bagaimana developer baru (atau agen AI) menemukan kodenya?** Struktur folder harus bisa menjelaskan dirinya sendiri.
Tetap Realistis: Prinsip "Cukup Baik"
Saya tidak ingin kamu selesai membaca postingan ini lalu berpikir aplikasimu butuh enam lapisan abstraksi sebelum bisa menambahkan tombol login. Systems design itu tentang *proporsi*.
Aplikasi utilitas dua layar tidak butuh Riverpod dengan code generation. Ia butuh `StatefulWidget` dan repository. Aplikasi e-commerce produksi tidak butuh tiga puluh provider untuk merender daftar produk. Ia butuh batasan yang jelas, kepemilikan state yang disengaja, dan data layer yang tidak bocor ke UI.
Engineer senior yang baik tahu kapan *tidak* menerapkan kompleksitas. Agen AI tidak — mereka dengan senang hati akan membuatkan factory pattern untuk fitur yang seharusnya cukup empat puluh baris. Tugasmu adalah menjadi pagar pengamannya.
Penutup
Inilah yang membedakan engineer Flutter junior dari senior di era AI: **junior menyuruh AI membangun aplikasinya, lalu mencoba memperbaiki apa yang rusak. Senior mendesain batasan aplikasinya, lalu membiarkan AI membangun di dalam batasan itu.**
Adopsi pola tiga lapis. Tulis spec pendek sebelum melepas agen. Tinjau dengan niat. Desain untuk kegagalan terlebih dahulu. Dan ingat: sistem itu tentang *apa yang kamu bangun*, bukan tentang AI yang menuliskannya.
Karena entah itu manusia atau language model yang mengetik kodenya, tetap ada seseorang yang harus memutuskan di mana state tinggal. Orang itu seharusnya kamu.
FAQ
1. Apakah saya benar-benar butuh "systems design" untuk Flutter? Bukankah itu urusan backend?
Frontend systems design itu nyata. Ini tentang memutuskan di mana state tinggal, bagaimana data mengalir, dan di mana menaruh batasan antara widget, logic, dan data layer. Backend design menangani skala; frontend design menangani *kompleksitas*. Aplikasi Flutter dengan lima puluh widget yang saling bergantung dan tanpa batasan sama sulitnya dipelihara seperti backend dengan service-spaghetti.
2. Bagaimana cara menghadapi systems design di interview Flutter?
Fokus pada pemisahan concern. Pewawancara ingin mendengar kamu berbicara tentang dekomposisi widget tree, kepemilikan state (mana yang lokal vs global), kontrak data (apa yang dikembalikan repository?), dan penanganan kegagalan. Menyebut tools seperti Riverpod atau Bloc itu bagus, tetapi menyebut *kapan kamu akan memakai yang mana* lebih baik. Dan jangan terburu-buru menulis kode — bicarakan trade-off dulu.
3. Haruskah saya membiarkan agen AI mendesain arsitektur Flutter dari nol?
Tidak. Agen hebat dalam mengimplementasi di dalam batasan, tetapi mereka akan dengan senang hati menaruh panggilan database di dalam widget kalau promptnya ambigu. Tulis spec pendek — bahkan sekadar poin-poin — yang mendefinisikan folder, hook manajemen state, dan kontrak data *sebelum* agen mulai. Lalu tinjau hasilnya berdasarkan spec itu. Spec kamu adalah tuas kendali paling penting.
4. Mana yang lebih baik untuk aplikasi Flutter besar: Riverpod atau Bloc?
Keduanya siap produksi; ini lebih merupakan pertanyaan filosofi daripada pertanyaan teknis. Riverpod memberi keamanan compile-time, scoped providers, dan gaya yang lebih fungsional. Bloc memberlakukan pola event/state yang ketat dan lebih mudah didokumentasikan dengan konsekuensi verbositas. Untuk aplikasi besar, pilih berdasarkan kenyamanan timmu lalu konsisten. Menerapkan salah satunya secara konsisten lebih baik daripada berganti-ganti di tengah proyek. Baca kedua dokumentasinya — [riverpod.dev](https://riverpod.dev) dan [bloclibrary.dev](https://bloclibrary.dev) — dan putuskan bersama timmu.
Teknoloji
Comments (0)
No comments yet. Be the first to comment!
Leave a Comment