İçeriğe atla

Google Closure Compiler: Mirası, Dersler ve Modern Alternatifler

Google'ın 2009'da çıkardığı Closure Compiler'ın modern web araçlarını nasıl şekillendirdiğini ve bugünkü build araçlarına kalıcı etkisini inceliyoruz.

Ayhan Sipahi Ayhan Sipahi

Google’ın Closure Compiler’ı 2009’da JavaScript’e tüm program kapsamında statik analizi getirdi: dosyalar arası dead code elimination ve JSDoc tabanlı type checking. Bu, webpack, esbuild ya da TypeScript bu yetenekleri yaygınlaştırmadan yıllar önceydi. Bu kökenin farkında olmayan ekipler aynı fikirleri sık sık yeniden keşfediyor ve yeni araçlara atfediyor.

2009’da Tüm Program Kapsamında Analiz#

Gelişmiş Dead Code Elimination#

Closure Compiler fonksiyon çağrılarını tüm codebase’iniz boyunca takip ediyor, hiçbir yerden referans verilmeyen kodu tamamen kaldırıyordu. Dönemin diğer araçları boşluk kaldırmak ve değişken yeniden adlandırmakla yetiniyordu:

// Closure Compiler öncesi
function calculateTax(amount, rate) {
  return amount * rate;
}

function formatCurrency(amount) {
  return '$' + amount.toFixed(2);
}

function processOrder(order) {
  const tax = calculateTax(order.amount, 0.08);
  return order.amount + tax; // formatCurrency hiç çağrılmıyor!
}

// Advanced compilation sonrası
function a(b){return 1.08*b.amount}

Compiler, formatCurrency fonksiyonunu hiç çağrılmadığını ispatlayabildiği için tamamen kaldırırdı.

TypeScript’ten Önce Type Checking#

Closure Compiler ayrıca JSDoc annotation’ları ile ifade edilen bir type sistemi sunuyordu:

/**
 * @param {number} width
 * @param {number} height
 * @return {number}
 */
function calculateArea(width, height) {
  return width * height;
}

/**
 * @param {string} name
 * @param {!Array<number>} scores
 */
function processStudent(name, scores) {
  // Compiler burada type uyumsuzluklarını yakalar
  const area = calculateArea(name, scores); // Hata!
}

Closure Library Mimarisi#

Closure Library, Google’ın karmaşık web uygulamaları geliştirme sorununun cevabıydı. Bugün oldukça modern görünen component-based bir mimari sağlıyordu:

// Closure Library component pattern'i (2010 civarı)
goog.provide('myapp.UserProfile');
goog.require('goog.ui.Component');
goog.require('goog.dom');

/**
 * @constructor
 * @extends {goog.ui.Component}
 */
myapp.UserProfile = function() {
  goog.ui.Component.call(this);
};
goog.inherits(myapp.UserProfile, goog.ui.Component);

myapp.UserProfile.prototype.createDom = function() {
  this.setElementInternal(
    goog.dom.createDom('div', 'user-profile')
  );
};

Bu dependency management sistemi (goog.provide ve goog.require) aslında erken bir modül sistemiydi ve hem AMD’nin hem de CommonJS’in yaygınlaşmasından önce geliyordu. Compiler bu modül yapısını kullanarak tüm codebase’i analiz ediyor, dosya sınırlarını aşarak erişilemeyen kodu atıyordu: bugünkü tree shaking’in atası.

Performans Kazanımı Nereden Geliyordu#

Kazanç birkaç somut mekanizmadan geliyordu. ADVANCED modu, dahili olduğunu ispatlayabildiği her property ve değişkeni yeniden adlandırıyor, küçük fonksiyonları inline ediyor ve çağrı grafiğinin hiç ulaşmadığı dalları siliyordu. jQuery döneminin codebase’lerinde taşınan ağırlığın büyük kısmı kimsenin çağırmadığı kütüphane yüzeyiydi; bu kombinasyon bundle’ın önemli bir bölümünü kesiyordu. İkinci kazanç compile-time type checking’di: uyumsuzluklar tarayıcıya ulaşmadan, build sırasında ortaya çıkıyordu.

Yine de migration sancılıydı. Öğrenme eğrisi dikti, optimize edilmiş çıktıyı debug etmek daha da zordu. Production’da bir şeyler ters gittiğinde şöyle hatalar görürdünüz:

TypeError: Cannot read property 'a' of undefined at b.c (compiled.js:1:23847)

Source map’ler o dönemde henüz erken geliştirme aşamasındaydı ve production hatalarını orijinal kodla ilişkilendirmek zordu. Bu debugging zorluğu, Closure’ın benimsenmesini ciddi şekilde engelledi.

Closure Araçlarının Düşüşü#

Araçlar çevresindeki her şeyin teknik olarak önündeydi ve yine de mindshare kaybettiler.

Developer Experience Açığı#

Araçlar güçlü olsa da önemli bir mindset değişimi gerektiriyordu. JSDoc annotation’larının verbosity’si, o dönemde popüler olan loose, dynamic JavaScript’e kıyasla ağır geliyordu:

// Developer'ların yazmak istediği
function add(a, b) {
  return a + b;
}

// Closure'ın optimizasyon için gerektirdiği
/**
 * @param {number} a
 * @param {number} b
 * @return {number}
 */
function add(a, b) {
  return a + b;
}

Ekosistemin Parçalanması#

JavaScript ekosistemi CommonJS’e ve daha sonra ES modüllerine doğru ilerliyordu. Closure’ın goog.provide/goog.require sistemi giderek izole kalıyordu:

2009: Çoklu Modül Sistemleri

Closure goog.require

AMD/RequireJS

CommonJS/Node.js

İzole Ekosistem

Tarayıcıda Yaygınlaşma

2015: ES6 Modülleri

Kademeli Düşüş

Modern Bundler'lar

Build Kurulumunun Karmaşıklığı#

Closure Compiler’ı kurmak kolay değildi. Tipik bir build konfigürasyonu şöyle görünürdü:

// closure-build.js (basitleştirilmiş versiyon)
const compiler = require('google-closure-compiler').compiler;

new compiler({
  js: 'src/**.js',
  compilation_level: 'ADVANCED_OPTIMIZATIONS',
  externs: 'externs/jquery.js',
  warning_level: 'VERBOSE',
  jscomp_error: 'checkTypes',
  output_wrapper: '(function(){%output%})();'
});

Bunu, “zero configuration” modunu getiren webpack 4 ile karşılaştırın.

Araçlardan Uzun Yaşayan Fikirler#

Her modern bundler’ın öne çıkardığı tree shaking, Closure Compiler’ın öncülük ettiği dead code elimination’ın ta kendisi:

// Modern tree shaking (webpack/rollup)
import { debounce } from 'lodash'; // Sadece debounce'u import eder

Terser ve esbuild aynı optimizasyon tekniklerinin çoğunu daha iyi bir developer experience ile uyguluyor. TypeScript’in popülaritesi de developer’ların JavaScript’te type safety istediğini kanıtladı; annotation’ları, Closure’ın JSDoc ile ifade ettiği fikri taşıyor:

// TypeScript (modern)
function calculateArea(width: number, height: number): number {
  return width * height;
}

// vs Closure JSDoc (2009)
/**
 * @param {number} width
 * @param {number} height  
 * @return {number}
 */
function calculateArea(width, height) {
  return width * height;
}

Closure Araçları Bugün#

Yeni bir projede Closure araçları nadiren doğru seçim. Ekosistem ilerlemiş durumda ve modern alternatifler çoğu use case için daha iyi developer experience sağlıyor. Google, Closure Library deposunu 2024’te arşivledi; Closure Compiler’ın bakımı ise devam ediyor.

Zaten Closure araçları kullanan bir proje üzerinde çalışıyorsanız migration için acele etmeyin. Google’ın birçok ürünü bunları yıllarca başarıyla kullandı. Araçlar kararlı ve takımınız onları iyi biliyorsa sırf araç tercihi migration maliyetini haklı çıkarmaz.

Öğrenme amaçlı, compiler’ın kaynak kodu statik analiz ve optimizasyon teknikleri için hala okumaya değer.

Kaynaklar#

İlgili yazılar