Integrasi Large Language Models (LLM) dalam alur kerja rekayasa perangkat lunak mempercepat penulisan antarmuka pengguna berbasis Tailwind CSS. Namun, tanpa pembatasan ketat, LLM cenderung menghasilkan AI slop code: ribuan variasi kelas arbitrary (seperti w-[341px], text-[#1a1a1a], hingga inline calc kompleks) yang gagal memanfaatkan deduplikasi utilitas standar Tailwind. Dampaknya langsung terlihat pada peningkatan tajam ukuran bundle CSS, waktu kompilasi CI/CD yang lambat, dan degradasi performa render di sisi klien.

Artikel ini menyajikan alur observasi, integrasi gating di CI/CD, protokol rollback otomatis pada canary release, serta skrip validasi untuk mencegah kebocoran CSS bloat ke production.

Root Cause: Mengapa Template LLM Memicu Bloat

Tailwind CSS v3 dan v4 mengandalkan deteksi string statis berbasis regular expression pada berkas template. Mesin JIT (Just-In-Time) Tailwind men-generate aturan CSS untuk setiap token unik yang cocok dengan pola kelas CSS.

Ketika LLM menulis komponen, model tidak memiliki memori global terhadap sistem desain proyek. Karakteristik umum kode Tailwind yang dihasilkan LLM mencakup:

  • Arbitrary Value Sprawl: Alih-alih menggunakan p-4 atau p-6, LLM sering menghasilkan p-[18px], p-[19px], atau m-[21px]. Setiap variasi arbitrer memaksa JIT mencetak kelas baru di stylesheet final.
  • Redundant Specificity: Menumpuk modifier arbitrary seperti hover:focus:active:bg-[#f3f4f6] secara inkonsisten.
  • Dead State Clutter: Menghasilkan markup dummy dengan kombinasi kelas yang tidak pernah dipakai ulang di komponen lain, meniadakan efisiensi kompresi Gzip/Brotli.

Hasilnya, file CSS hasil kompilasi yang seharusnya berukuran 15-30 kB gzip dapat melonjak hingga ratusan kilobyte, memperlambat proses First Meaningful Paint (FMP) secara signifikan.

Observasi Bundle Size dan Waktu Kompilasi

Langkah pertama mitigasi adalah mendeteksi anomali pada tahap kompilasi. Gunakan visualizer pada bundler (seperti Vite, Rollup, atau Webpack) untuk melacak pertumbuhan CSS asset.

Jika menggunakan Vite, pasang rollup-plugin-visualizer:

// vite.config.ts
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    visualizer({
      filename: 'stats.html',
      template: 'treemap',
      gzipSize: true,
      brotliSize: true,
      emitFile: false,
    }),
  ],
});

Eksekusi build akan menghasilkan stats.html. Pantau node chunk CSS. Jika chunk CSS memuat ribuan selector turunan dengan bracket [...], basis kode Anda telah terkontaminasi arbitrary bloat.

Automated Budget Check di Pipeline CI

Pencegahan paling efektif dilakukan di pull request. Buat batas ambang (budget) ketat menggunakan CLI sederhana yang menghentikan pipeline jika aset CSS melebihi batas batas ukuran yang diizinkan (misalnya, maksimal 50 kB pasca-gzip).

Berikut adalah skrip Node.js mandiri untuk memvalidasi ukuran file CSS hasil kompilasi:

// scripts/check-css-budget.mjs
import fs from 'node:fs';
import path from 'node:path';
import zlib from 'node:zlib';

const DIST_DIR = path.resolve('dist/assets');
const MAX_GZIP_SIZE_KB = 45; // Ambang batas maksimal

const cssFiles = fs.readdirSync(DIST_DIR).filter(file => file.endsWith('.css'));

if (cssFiles.length === 0) {
  console.error('Error: Tidak ada file CSS ditemukan di dist/assets');
  process.exit(1);
}

let hasViolation = false;

for (const file of cssFiles) {
  const filePath = path.join(DIST_DIR, file);
  const content = fs.readFileSync(filePath);
  const gzipSizeKb = zlib.gzipSync(content).length / 1024;

  console.log(`Checking ${file}: ${gzipSizeKb.toFixed(2)} KB (gzip)`);

  if (gzipSizeKb > MAX_GZIP_SIZE_KB) {
    console.error(`VIOLATION: ${file} melebihi batas ${MAX_GZIP_SIZE_KB} KB!`);
    hasViolation = true;
  }
}

if (hasViolation) {
  process.exit(1);
}

console.log('Semua aset CSS berada dalam batas performa.');
process.exit(0);

Jalankan skrip ini langsung setelah tahap npm run build di pipeline GitHub Actions atau GitLab CI.

Gating Deployment dan Otomasi Rollback Canary

Jika aset berlebih lolos ke deployment canary (misalnya, rilis 10% trafik), metrik performa real-user (Core Web Vitals, khususnya FID/INP dan FCP) atau metrik ukuran asset dari response header edge runtime harus memicu rollback instan.

Diagram alur penanganan canary deployment:

Build & Artifact Generation
       │
       ▼
[CI CSS Budget Check] ──(Gagal)──► Abort PR / Build
       │ (Lolos)
       ▼
Deploy ke Canary (10% Traffic)
       │
       ▼
[Synthetic / Edge Monitoring]
       │
       ├──(Asset Size > Threshold / FCP Drop)──► Trigger Automated Rollback API
       │
       ▼ (Metrik Normal)
Promote Canary ke 100% Production

Contoh implementasi GitHub Actions job untuk memicu rollback otomatis saat edge check mendeteksi ukuran CSS melampaui batas pada target canary:

# .github/workflows/canary-monitor.yml
name: Canary Asset Gating

on:
  deployment_status:

jobs:
  verify-canary:
    if: github.event.deployment_status.state == 'success' && github.event.deployment.environment == 'canary'
    runs-on: ubuntu-latest
    steps:
      - name: Inspect Canary Asset Payload
        run: |
          CSS_URL="https://canary.example.com/assets/index.css"
          SIZE_BYTES=$(curl -sI -H "Accept-Encoding: gzip" "$CSS_URL" | grep -i content-length | awk '{print $2}' | tr -d '\r')
          MAX_ALLOWED_BYTES=46080 # 45 KB

          echo "Canary CSS Content-Length: $SIZE_BYTES bytes"

          if [ "$SIZE_BYTES" -gt "$MAX_ALLOWED_BYTES" ]; then
            echo "Asset melampaui batas! Memulai Canary Rollback..."
            curl -X POST "https://api.example.com/v1/deployments/rollback" \
              -H "Authorization: Bearer ${{ secrets.DEPLOY_TOKEN }}" \
              -d '{"environment": "canary", "reason": "CSS bundle size anomaly detected"}'
            exit 1
          fi

Prevensi: Linter untuk Arbitrary Value Scanner

Pencegahan terbaik adalah tidak mengizinkan kelas arbitrary Tailwind tak terkendali masuk ke commit tree. Buat skrip pra-commit atau pemeriksaan pull request untuk membatasi pemakaian tanda kurung siku [...] pada markup komponen.

// scripts/scan-arbitrary-classes.mjs
import fs from 'node:fs';
import glob from 'glob';

// Menargetkan komponen UI
const files = glob.sync('src/**/*.{jsx,tsx,vue,html}');
const ARBITRARY_REGEX = /class(?:Name)?=["'][^"']*\[[^\]]+\][^"']*["']/g;

let violations = 0;

for (const file of files) {
  const content = fs.readFileSync(file, 'utf-8');
  const matches = content.match(ARBITRARY_REGEX);

  if (matches) {
    console.warn(`[WARNING] Arbitrary values ditemukan di ${file}:`);
    matches.forEach(m => console.warn(`   ${m}`));
    violations += matches.length;
  }
}

if (violations > 5) { // Toleransi maksimal arbitrary class baru
  console.error(`Gagal: Ditemukan ${violations} arbitrary Tailwind classes. Harap gunakan theme token.`);
  process.exit(1);
}

console.log('Scan markup valid. Tidak ada degradasi token theme.');

Postmortem Singkat: Insiden CSS Bloat

Insiden: Peningkatan 380% ukuran bundle CSS di release tag v2.4.1.
Dampak: First Contentful Paint (FCP) mobile meningkat 1.2 detik. Waktu compile asset di CI melonjak dari 42 detik menjadi 3 menit 15 detik.
Root Cause: PR migrasi halaman landing baru di-generate sepenuhnya via LLM. Model membuat ratusan variasi warna hex arbitrary (text-[#...], bg-[#...]) dan margin spesifik (m-[...]) alih-alih mengonfigurasi tailwind.config.js atau memakai palet bawaan.
Action Items:
1. Konfigurasi CI Budget Enforcer pada seluruh pull request.
2. Terapkan ESLint plugin eslint-plugin-tailwindcss dengan rule tailwindcss/no-custom-classname diset ke warn atau error.
3. Otomasi canary health-check dengan eksekusi rollback instan via webhook.

Memanfaatkan output LLM menuntut tata kelola tooling yang lebih disiplin. Melalui gating deterministik di CI/CD dan linting berbasis aturan desain, efisiensi ekosistem utility-first Tailwind tetap terjaga tanpa mengorbankan kecepatan delivery kode.