Hydration mismatch terjadi karena markup server-side rendering (SSR) tidak cocok dengan apa yang di-render di klien. Di FreeBSD 16, tujuan utama adalah menjaga konsistensi markup dan state saat deploy serta mematuhi kebijakan GPL-free baru. Solusinya adalah menjaga dependency yang kompatibel GPL-free, membangun ulang dengan deterministik, dan sistem verifikasi untuk mendeteksi perbedaan server-klien.

Dalam panduan ini, kita akan membahas dampak kebijakan FreeBSD 16 yang menghilangkan paket GPL, bagaimana memvalidasi state server-klien, mendeteksi markup yang berbeda, serta strategi rollout yang aman tanpa mengandalkan library GPL.

1. Dampak FreeBSD 16 GPL-Free pada Dependency dan Build

FreeBSD 16 yang kini “GPL-free” berarti paket di repositori utama tidak lagi mengandung komponen di bawah lisensi GPL. Untuk tim frontend/DevOps, ini berimplikasi langsung pada pemilihan dependency yang dibutuhkan untuk build SSR. Hindari paket berbasis GPL dengan beralih ke alternatif MIT/Apache, misalnya:

  • Gunakan Vite atau Esbuild untuk bundling karena lisensinya permissive.
  • Pilih middleware server yang menggunakan lisensi Apache/MIT (misalnya fastify bukan yang bergantung GPL).

Dalam konteks build deterministik, hindari dependency yang mengandalkan binary non-deterministik atau waktu kompilasi yang tidak stabil. Jalankan build di kontainer FreeBSD yang dikontrol, dan gunakan checksum keluaran (sha256) untuk memastikan bundle tidak berubah antar deploy:

# Build bundle SSR
npm run build:ssr
sha256sum dist/server.mjs dist/client/entry.mjs

Jika checksum berubah padahal tidak ada perubahan kode, lengkapi proses dengan menelusuri dependensi transitive atau memilih resolver yang menyediakan versi spesifik tanpa ^ atau ~.

2. Validasi State Server-Client

Hydration mismatch paling sering disebabkan state awal yang berbeda antara server dan klien. Buat kontrak eksplisit tentang data apa saja yang di-generate server dan diharapkan saat hydrate.

2.1 Sinkronisasi JSON State

Ekspor state awal dari server dalam format JSON, lalu bandingkan dengan state yang diterima klien. Contoh pendekatan:

const initialState = await fetchDataForRoute(route);
app.use((req, res) => {
  const stateJson = JSON.stringify(initialState);
  res.send(`
    
  `);
});

Di klien, parsing data ini harus menghasilkan struktur yang sama persis. Kunci di sini adalah bentuk data (urutan properti, tipe nilai). Gunakan Object.freeze di server untuk mencegah penyimpanan data non-serializable.

2.2 Validasi Otomatis

Tambah tes integrasi yang memuat halaman SSR dan memeriksa JSON state sebelum hydration berlangsung:

test('initial state matches server snapshot', async () => {
  const html = await renderPage('/dashboard');
  const state = extractInitialState(html);
  expect(state).toEqual(await expectedStateForRoute('/dashboard'));
});

Jika state berbeda, tes ini gagal sebelum kode mencapai browser. Pastikan expectedState diambil dari fixture deterministik, misalnya menggunakan fixtures/server-state/.

3. Deteksi Markup yang Berbeda

Markup yang berbeda memicu hydration mismatch. Gunakan pendekatan berikut:

  • Jalankan render server di lingkungan staging dan simpan HTML output.
  • Jalankan render klien (sebelum hydrate) dengan alat headless seperti playwright atau jsdom dan bandingkan.

Contoh skrip sederhana untuk membandingkan output:

curl -s https://staging.example.com/page > server.html
node renderClient.js > client.html
wdiff -n server.html client.html

renderClient.js dapat meniru perilaku render klien dengan mengambil bundle yang sama yang akan di-load di browser. Perhatian: wdiff menunjukkan perbedaan tekstual, tetapi untuk struktur DOM, gunakan validator HTML seperti html-validate atau snapshot testing.

Jika terdapat perbedaan, telusuri apakah:

  • Komponen menggunakan data random atau waktu lokal tanpa normalisasi.
  • Rendering bersyarat tidak konsisten (misalnya bergantung pada browser-only API).
  • Terdapat side-effect di render server seperti memodifikasi document.

4. Strategi Rollout Tanpa Library GPL

Karena FreeBSD 16 tidak memasukkan library GPL, rencanakan rollout dengan komponen permissive. Strategi berikut membantu memitigasi risiko hydration mismatch:

  • Canary deployment: Deploy bundle SSR ke subset node, validasi dengan monitoring otomatis terhadap log hydration error (misalnya memantau console.error yang berisi "Hydration failed" di klien).
  • Feature flag: Aktifkan SSR untuk pages tertentu secara bertahap. Jika mismatch terdeteksi, segera rollback flag sehingga client fallback pada CSR.
  • Build reproducibility check: Setelah build selesai, jalankan script sha256sum pada bundle dan bandingkan dengan artefak snapshot di repositori. Ini memastikan tidak ada plugin build berbasis GPL yang menyuntikkan variasi.

Selama rollout, pastikan tooling observability (seperti log tersentralisasi atau Sentry) menangkap stacktrace hydration error. Gunakan release tagging untuk mengidentifikasi versi yang menyebabkan mismatch.

5. Checklist Debug Hydration

Gunakan daftar pemeriksaan berikut setiap kali menghadapi hydration mismatch:

  • Validasi JSON state: Apakah data server dan klien identik? Gunakan JSON.stringify dengan sort keys.
  • Periksa markup: Apakah ada atribut atau teks yang berubah antara SSR dan CSR?
  • Konsistensi environment: Apakah proses build menggunakan variable lingkungan (time zone, locale) yang berbeda?
  • Dependency licensing: Pastikan package yang terlibat tidak GPL dan tidak menyertakan binary yang tidak deterministik.
  • Logging: Hidupkan log hydrated components via useEffect di klien untuk menangkap komponen bermasalah.
  • Regression test: Jalankan test SSR snapshot setelah perubahan dependency atau Node.js upgrade.

Debug tools yang relevan antara lain:

  • Playwright untuk render headless dan periksa DOM.
  • html-validate untuk memastikan struktur HTML konsisten.
  • grep/rg untuk menemukan referensi ke API browser di kode server.
  • wdiff atau diffoscope untuk membandingkan bundle hasil build.

Penutup

Dengan pendekatan deterministik, pengawasan state yang ketat, deteksi markup, dan rollout bertahap tanpa library GPL, tim frontend/DevOps dapat mengurangi risiko hydration mismatch saat deploy SSR di FreeBSD 16. Fokus pada konsistensi build, detail state, dan observabilitas akan menjaga pengalaman pengguna tetap mulus.