İçeriğe atla

2025-09-04

Erken Web Dönemi: Script'lerin Basit Olduğu Zamanlar

Webpack var olmadan önce, dosyaları Grunt ile birleştiriyorduk. React'tan önce, jQuery spagettisiyle boğuşuyorduk. Frontend tooling'in manuel dosya yönetiminden sofistike build sistemlerine nasıl evrildiği burada.

Frontend tooling, birleştir-ve-minify problemi olarak başladı: elle yönetilen script sırası, deploy için shell script’leri ve manuel sürecin aşılmasıyla gelen ilk task runner dalgası (Make, sonra Grunt, sonra Gulp). Bu erken tooling üzerindeki baskı build hızından değil, bağımlılık sıralaması ve tarayıcılar arası uyumluluktan geliyordu: yüklenen bir site ile sessiz JavaScript hataları üreten bir site arasındaki fark.

Bu yazı, frontend tooling’in evrimini inceleyen dört parçalık serinin ilkidir. Elle yazılmış script’lerin ve tarayıcıya özel hack’lerin build öncesi dönemini (1995-2006), bir uyumluluk katmanı olarak jQuery’nin ortaya çıkışını ve deterministik bir build boru hattı fikrini getiren task-runner kuşağını (Grunt, Gulp) ele alır.

Pre-jQuery Karanlık Çağı (1995-2006)

Modern tooling öncesi web development tamamen farklı bir dünyaydı. JavaScript ciddi uygulamalar için kullanacağınız bir dil değildi. Image rollover’lar ve form validation için kullanılırdı - eğer iddialıysanız.

Manuel Dosya Yönetimi Kabusları

2000’lerin başında, tipik bir proje yapısı şöyleydi:

website/
├── index.html
├── contact.html
├── js/
│  ├── utils.js
│  ├── validation.js
│  ├── slideshow.js
│  └── main.js
├── css/
│  ├── reset.css
│  ├── layout.css
│  └── styles.css
└── images/

Ve HTML’iniz şu canavarlık gibiydi:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/layout.css">
    <link rel="stylesheet" href="css/styles.css">
</head>
<body>
    <!-- content -->
    
    <script src="js/utils.js"></script>
    <script src="js/validation.js"></script>
    <script src="js/slideshow.js"></script>
    <script src="js/main.js"></script>
</body>
</html>

Problemler anında ve acı vericiydi:

  1. Sıra bağımlılığı cehennemi: main.js’i utils.js’den önce taşı ve her şey bozulur
  2. Global namespace kirliliği: Her değişken global scope’ta yaşardı
  3. Dependency management yok: Hangi script’in hangi script’e ihtiyacı olduğunu manuel takip etmek zorundaydınız
  4. Performance: JavaScript dosyaları için 15 ayrı HTTP request normalti
  5. Cache busting: Dosya yollarına manuel olarak ?v=1.2.3 eklemek
  6. Development vs production: Farklı environment’lar için farklı HTML dosyaları

Bir kez, slideshow’un production’da neden bozulduğunu debug etmek için bütün bir gün harcadım, sadece production server’ın case-sensitive olduğunu ve bir script tag’de SlideShow.js yerine slideshow.js yazması gerektiğini keşfetmek için. Bunlar zamanımızı tüketen problemlerdi. Development ve production arasındaki bu tür küçük farklar büyük debug seanslarına yol açıyordu.

Browser Uyumluluğu: Internet Explorer Yılları

Cross-browser uyumluluğu sadece “güzel olur” değildi - her teknik kararı şekillendiren birincil kısıttı. Internet Explorer 6’nın %60 market share’i vardı ve web standartlarının kendi yorumu vardı.

Günlük yazdığımız kod türü:

function addEvent(element, event, handler) {
    if (element.addEventListener) {
        element.addEventListener(event, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + event, handler);
    } else {
        element['on' + event] = handler;
    }
}

function getXMLHttpRequest() {
    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        try {
            return new ActiveXObject('Msxml2.XMLHTTP');
        } catch (e) {
            return new ActiveXObject('Microsoft.XMLHTTP');
        }
    }
    return null;
}

Her basit operasyon browser detection ve fallback’ler gerektiriyordu. Feature’lar inşa etmekten çok compatibility layer’ları yazmaya zaman harcıyorduk. Bu yaklaşım maintainability’yi zorlaştırıyordu çünkü her yeni browser versiyonu yeni edge case’ler getiriyordu.

jQuery Devrimi (2006-2012)

jQuery 2006’da geldiğinde, büyü gibi hissettirdi. John Resig browser uyumluluğu problemini “daha az yaz, daha fazla yap” diyen zarif bir API ile çözmüştü. Ama daha da önemlisi, jQuery daha sonra modern tooling’de temel olacak konseptleri tanıttı.

Abstraction’ın Gücü

// jQuery öncesi
function fadeOut(element) {
    var opacity = 1;
    var timer = setInterval(function() {
        if (opacity <= 0.1) {
            clearInterval(timer);
            element.style.display = 'none';
        }
        element.style.opacity = opacity;
        element.style.filter = 'alpha(opacity=' + opacity * 100 + ")";
        opacity -= opacity * 0.1;
    }, 50);
}

// jQuery ile
$('#myElement').fadeOut();

Bu sadece syntactic sugar değildi - felsefi bir değişimdi. jQuery abstraction layer’larının gücü feda etmeden karmaşıklığı gizleyebileceğini kanıtladı. Bu konsept daha sonra her frontend framework’ü etkileyecekti.

jQuery Plugin’leri: İlk Package Ecosystem

jQuery plugin’leri web’in ilk gerçek package ecosystem’iydi. Datepicker mi lazım? $('#myInput').datepicker(). Carousel mi istiyorsun? $('.carousel').slick().

Ama tooling perspektifinden işin ilginç kısmı buradaydı: jQuery plugin’lerini yönetmek vanilla JavaScript ile sahip olduğumuz aynı problemleri daha büyük ölçekte ortaya çıkardı.

2010’da tipik bir jQuery projesi şöyleydi:

<script src="js/jquery-1.4.2.min.js"></script>
<script src="js/jquery.ui.core.js"></script>
<script src="js/jquery.ui.datepicker.js"></script>
<script src="js/jquery.slick.min.js"></script>
<script src="js/jquery.validation.min.js"></script>
<script src="js/jquery.fancy-input.js"></script>
<script src="js/app.js"></script>

Problemler Başarıyla Birlikte Büyür

jQuery projeleri büyüdükçe, saf JavaScript developer’larının karşılaşmadığı yeni problemler ortaya çıktı:

Plugin Çakışmaları: İki plugin de $.fn.slider’ı modify ediyordu ve hangisinin kazandığını debug etmek kabustu.

Version Cehennemi: jQuery UI 1.8, jQuery 1.4 ile çalışıyordu ama 1.5 ile değil. jQuery’yi upgrade etmek potansiyel olarak her plugin’i bozabilirdi.

Performance Sorunları: 15 jQuery plugin’i yüklemek, her plugin’in fonksiyonalitesinin sadece %10’unu kullanıyor olabileceğiniz halde 300KB JavaScript indirmek demekti.

Module System Yok: Her şey hala globaldi. $.myPlugin, $.myOtherPlugin ile çakışıyordu ve büyük uygulamalarda scope sorunlarını debug etmek vahşetti.

2011’de üzerinde çalıştığım bir production uygulamasından kod parçası:

$(document).ready(function() {
    // 12 farklı plugin'i initialize et
    $('.datepicker').datepicker();
    $('.slider').slider();
    $('.tooltip').tooltip();
    $('.validate').validate();
    $('.tabs').tabs();
    $('.accordion').accordion();
    $('.modal').modal();
    $('.carousel').carousel();
    $('.autocomplete').autocomplete();
    $('.sortable').sortable();
    $('.draggable').draggable();
    $('.charts').charts();
    
    // Sonra 500 satır custom JavaScript
    // her yerde global state'i modify ediyor
});

Takımlar büyüdükçe bunu yönetmek imkansız hale geldi. Güvenli refactor yapamıyordunuz çünkü herhangi bir değişiklik başka bir developer’ın global state’e bağımlı kodunu bozabilirdi. Code review süreçleri bile bu karmaşıklık nedeniyle zorlaşıyordu. jQuery’nin başarısı cross-browser uyumluluk ve DOM manipülasyonunu basitleştirmekti—ama bu abstraction, dependency management ve modülerlik sorunlarını çözmedi.

”Build Tool”ların İlk Denemeleri

2009-2010’da, akıllı developer’lar pattern’leri fark etmeye ve çözümler inşa etmeye başladılar. Bunlar bugün bildiğimiz sofistike build tool’lar değildi, ama manuel dosya yönetiminin sürdürülebilir olmadığının ilk fark edilişiydi.

Shell Script’ler ve Makefile’lar

İlk “build tool”lar tam anlamıyla shell script’lerdi:

#!/bin/bash
# build.sh

echo "Building application..."

# CSS dosyalarını birleştir
cat css/reset.css css/layout.css css/styles.css > dist/app.css

# JS dosyalarını birleştir
cat js/utils.js js/validation.js js/main.js > dist/app.js

# Minify et (YUI Compressor için Java kuruluysa)
java -jar yuicompressor-2.4.7.jar dist/app.css -o dist/app.min.css
java -jar yuicompressor-2.4.7.jar dist/app.js -o dist/app.min.js

echo "Build complete!"

Sofistike takımlar Makefile kullanırdı:

CSS_FILES = css/reset.css css/layout.css css/styles.css
JS_FILES = js/utils.js js/validation.js js/main.js

dist/app.css: $(CSS_FILES)
	cat $(CSS_FILES) > dist/app.css

dist/app.min.css: dist/app.css
	java -jar yuicompressor-2.4.7.jar dist/app.css -o dist/app.min.css

dist/app.js: $(JS_FILES)
	cat $(JS_FILES) > dist/app.js

dist/app.min.js: dist/app.js
	java -jar yuicompressor-2.4.7.jar dist/app.js -o dist/app.min.js

clean:
	rm -rf dist/*

build: dist/app.min.css dist/app.min.js

Çözemediğimiz Problemler

Bu erken build denemeleri temel concatenation ve minification problemlerini çözdü, ama daha derin sorunları ortaya çıkardı:

Dependency Resolution: Hala dosyaların sırasını manuel yönetiyorduk. main.js, utils.js’ten bir fonksiyona bağımlıysa, utils.js’i önce listelemeyi hatırlamak zorundaydık.

Incremental Build’ler: Her değişiklik her şeyi yeniden build etmek demekti. utils.js’te tek karakter değişikliği bütün JavaScript bundle’ını yeniden build’lemeyi tetikliyordu.

Asset Management: Resimler, fontlar ve diğer asset’ler hala elle yönetiliyordu. Cache busting manuel olarak dosya isimlerini güncellemeyi gerektiriyordu.

Development Workflow: Build adımı o kadar yavaştı ki çoğu developer development sırasında atlıyordu, bu da ayrı dosyalarla çalışırken işe yarayıp concatenate edilince bozulan klasik “benim makinemde çalışıyor” problemlerine yol açıyordu. Live reload veya hot module replacement gibi kavramlar o zamanlar hayal bile edilemezdi.

Bower Devrimi: Package Management Geldi

2012’de Twitter, Bower’ı release etti ve gelecek gibi hissettirdi. Sonunda frontend bağımlılıkları için gerçek bir package manager’ımız vardı:

bower install jquery
bower install jquery-ui
bower install bootstrap

ZIP dosyaları indirip manuel olarak projenize kopyalamak yerine, Bower bağımlılıkları otomatik halledecekti. Projeniz bir bower.json dosyası alıyordu:

{
  "name": "my-awesome-app",
  "dependencies": {
    "jquery": "~1.8.0",
    "jquery-ui": "~1.9.0",
    "bootstrap": "~2.3.0"
  }
}

Vaat ve Problemler

Bower indirme ve versiyon yönetimi problemini çözdü, ama yenilerini yarattı:

Flat Dependency Tree: Bower her şeyi bower_components/ içine nesting olmadan koyuyordu. İki package farklı jQuery versiyonlarına bağımlıysa, Bower size seçim yaptırıyordu - ve genelde bir şeyleri bozuyordu.

Build Integration Yok: Bower dosyaları indiriyordu, ama hala bunları HTML’inize veya build script’lerinize manuel eklemeniz gerekiyordu.

Version Çakışmaları: Flat dependency tree, dependency çakışmalarının developer’a itilmesi demekti. “Package A’nın jQuery 1.8’e ihtiyacı var, Package B’nin jQuery 1.9’a, sen hallet.”

2012’de tipik bir Bower projesi şöyleydi:

<!-- Development -->
<script src="bower_components/jquery/jquery.js"></script>
<script src="bower_components/underscore/underscore.js"></script>
<script src="bower_components/backbone/backbone.js"></script>
<script src="bower_components/bootstrap/js/bootstrap.js"></script>
<script src="js/models/user.js"></script>
<script src="js/views/userView.js"></script>
<script src="js/app.js"></script>

Production için concatenate ve minify etmek için hala build process’e ihtiyacınız oluyordu ve hala script sırasını manuel yönetmeniz gerekiyordu. Bower indirme işini kolaylaştırdı ama build pipeline’ı otomatikleştirmedi.

Geriye Bakış: Modern Tooling’i Şekillendiren Dersler

Bu dönemi yaşamak, endüstriye modern tooling tasarımını doğrudan etkileyen değerli dersler öğretti:

Manuel Prosesler Ölçeklenmiyor

Manuel dosya yönetimi gerektiren her çözüm, projeler büyüdükçe sonunda çöküyordu. Bu, modern build tool’ların automation-first yaklaşımına yol açtı.

Sıra Bağımlılıkları Şeytani

Script’lerin load sırasını manuel yönetmek zorunda olmak sürekli bug kaynağıydı. Bu kavrayış, bağımlılıkları explicit yapan module sistemlerine (CommonJS, AMD, ES modules) yol açtı.

Flat Dependency Tree’ler Çakışma Yaratır

Bower’ın flat dependency yaklaşımı çözdüğünden fazla problem yarattı. Bu deneyim npm’in nested dependency tree tasarımını ve pnpm’in sofistike resolution algoritmasını bilgilendirdi.

Developer Experience Önemli

Development ve production environment’ları arasındaki fark sürekli sürtünme yaratıyordu. Modern tool’lar hot reloading ve instant feedback loop’lar aracılığıyla dev/prod parity’yi prioritize ediyor.

Lock-in Olmayan Abstraction

jQuery’nin başarısı, altta yatan DOM’a doğrudan erişime izin verirken karmaşıklığı gizlemesinden geliyordu. Modern framework’ler bu pattern’i takip ediyor - escape hatch’leri olan güçlü abstraction’lar.

Devrim İçin Sahne Hazır

2012’ye kadar, frontend tooling’in çözmesi gereken çekirdek problemlerin çoğunu belirlemiştik:

  • Dependency management: Manuel script sıralaması sürdürülemezdi
  • Asset optimization: Concatenation ve minification açıkça gerekliydi
  • Development workflow: Development ve production arasındaki fark çok genişti
  • Browser compatibility: Abstraction layer’ları esastı
  • Code organization: Global scope pollution sona ermek zorundaydı

Ayrıca geleceği işaret eden erken çözümlerle deneyim yapmıştık:

  • Shell script’ler ve Makefile’larla build automation
  • Bower ile package management
  • jQuery ile abstraction layer’ları
  • Modülerliğin gücünü gösteren plugin ecosystem’ları

Bir sonraki devrim için sahne hazırdı: uygun task runner’lar, module bundler’lar ve modern JavaScript framework’lerinin doğuşu. Ama bu, bu serinin bir sonraki bölümünün hikayesi.

2012’de bilmediğimiz şey, bu problemlerin çözümlerinin frontend development hakkında düşünme şeklimizi temelden değiştireceğiydi. Sonraki birkaç yılda ortaya çıkan tool’lar sahip olduklarımızın sadece daha iyi versiyonları değildi—endüstriyi yeniden şekillendirecek tamamen yeni yaklaşımlardı. Grunt ve Gulp dependency sırası cehennemine çözüm getirecekti; RequireJS ve Browserify module sistemi kavramını tanıtacaktı.

Bir sonraki bölümde, build automation problemini çözmek için Grunt ve Gulp’un nasıl ortaya çıktığını, RequireJS ve Browserify’ın uygun module sistemlerini nasıl tanıttığını ve Webpack’in bundle’ı uygulama yaparak her şeyi nasıl değiştirdiğini keşfedeceğiz.

Frontend Tooling'in Evrimi: Bir Developer'ın Perspektifi

jQuery dosya birleştirmesinden Rust-powered bundler'lara - frontend tooling'in gerçek production problemlerini nasıl çözmek için evrildiğinin anlatılmamış hikayesi, öğrenilen dersler ve pratik deneyimlerle.

İlerleme 1 / 4 yazı

İlgili yazılar

Node.js ile Zaman Yönetimi: Moment.js Olmadan Zamana Hükmetmek

Production'da yaşanan zaman yönetimi problemleri, Moment.js'den modern alternatiflere geçiş stratejileri ve UTC handling best practice'leri. Timezone savaşlarından çıkışın yolu.

nodejsjavascripttime-management+7
Task Runner'lar ve Modern Bundling'in Doğuşu

Grunt'ın build automation'ı nasıl dönüştürdüğü ve Webpack'in dependency'ler hakkında düşünme şeklimizi nasıl devrim yaptığı. Frontend development'ı sonsuza dek değiştiren manuel processlerden sofistike bundling'e acı verici geçiş.

gruntgulpwebpack+5
Google Closure Compiler: Modern JavaScript Tooling'in Unutulan Öncüsü

Google'ın 2009'da yayınladığı Closure Compiler ve Library'nin modern web geliştirme araçlarını nasıl şekillendirdiğini, dead code elimination'dan type checking'e kadar olan etkilerini ve bugünkü build araçlarına olan kalıcı etkisini keşfediyoruz.

javascriptclosure-compilerbuild-tools+9
Harici Yetkilendirme Yönetim Sistemleri: Mimarınız İçin Doğru Platformu Seçmek

AWS Verified Permissions, SpiceDB, OpenFGA, Cerbos ve OPA dahil harici yetkilendirme platformlarının tarafsız değerlendirmesi. Mimari desenler, maliyet analizi ve mühendislik ekipleri için karar çerçevesi.

authorizationsecurityarchitecture+5
Cedar vs Rego vs OpenFGA: Politika Dili Karşılaştırması

Cedar, Rego, OpenFGA DSL ve Cerbos YAML/CEL politika dillerinin derinlemesine teknik karşılaştırması. Söz dizimi, performans kıyaslamaları, biçimsel doğrulama, araç desteği ve her dil için TypeScript entegrasyon örneklerini kapsar.

authorizationsecurityarchitecture+3