Grunt vs Gulp vs Webpack: Modern Bundling Nasıl Doğdu
Grunt'ın build automation'ı nasıl dönüştürdüğü ve webpack'in dependency anlayışını nasıl değiştirdiği: manuel süreçlerden modern bundling'e zorlu geçiş.
Manuel shell-script build pipeline’ları makineden makineye sessiz sedasız bozulur, kabile bilgisi biriktirir ve anlayan tek developer ayrıldığında çöker. Belgelenmemiş build hataları deployment’ları bloke eder ve toolchain’in kendisini bir risk kaynağına dönüştürür. Grunt buna declarative task otomasyonuyla yanıt verdi, Gulp ise configuration bloklarının yerine code stream’leri koydu. Browserify ve webpack de modeli task orchestration’dan dependency-graph bundling’e taşıdı.
Grunt bu ortama net bir ilerleme olarak girdi. İlk kez takımların elinde, karmaşık projeleri handle edecek kadar configure edilebilirken sıkıcı ve hata eğilimli işleri otomatize eden bir tool vardı.
Grunt Devrimi (2012-2015)#
Ben Alman 2012’de Grunt’ı release ettiğinde, temel bir şeyi ele alıyordu: build processlerinin imperative değil declarative olması gerekiyordu. Farklı makinelerde farklı çalışabilecek shell script’ler yazmak yerine, ne olmasını istediğinizi tarif ediyordunuz.
Script Yerine Konfigürasyon#
Tipik bir Gruntfile şöyleydi:
module.exports = function(grunt) {
grunt.initConfig({
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dist/built.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'dist/built.min.js': ['<%= concat.dist.dest %>']
}
}
},
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true,
console: true,
module: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
};
İlk kez, bir projeye bakıp build process sırasında tam olarak ne olduğunu anlayabiliyordunuz: artık gizemli shell script’ler yok, build’i yazan kişinin bunu düzgün dokümante etmiş olması için dua etmek yok.
Plugin Ekosistemi Patlaması#
Grunt’ın dehası, build task’lerinin pattern’leri takip ettiğini fark etmekti. Sass compile etmek mi lazım? grunt-contrib-sass var. Resimleri optimize etmek mi istiyorsun? grunt-contrib-imagemin. S3’e deploy etmek mi gerekiyor? grunt-aws-s3.
2013’e kadar yüzlerce Grunt plugin’i vardı. Neredeyse her şeyi otomatize edebiliyordunuz:
- CSS preprocessing (Sass, Less, Stylus)
- JavaScript linting ve minification
- Image optimization
- File copying ve watching
- Template compilation
- Testing framework’leri
- Deployment processleri
Pratikteki Etki#
İlk Grunt adoption’ları takımların deployment’a bakışını değiştirdi. Süreç “şansa bırak ve dua et”ten “grunt build çalıştır, kahveni al”a evrildi. Tekrarlanabilir bir task listesi insan hatasının bütün bir sınıfını da ortadan kaldırdı. Adımları kimse elle yazmadığı için minification’ı atlamak ya da lint’ten geçmemiş dosya göndermek artık mümkün değildi.
Daha da önemlisi, Grunt modern tool’ların hala takip ettiği pattern’i kurdu: code yerine configuration, plugin-based architecture ve development ile production build’leri arasında net ayrım.
Grunt’ın Sınırları#
Projeler büyüdükçe, Grunt’ın limitasyonları belirginleşti:
Configuration Cehennemi: Karmaşık Gruntfile’lar maintain edilemez hale geldi. Tek başına CSS tarafı şöyle görünebiliyordu:
// Sadece CSS kısmı; Gruntfile'ın tamamı birkaç yüz satırdı
sass: {
options: {
sourceMap: true,
outputStyle: 'compressed'
},
dev: {
files: {
'dist/css/main.css': 'src/scss/main.scss',
'dist/css/admin.css': 'src/scss/admin.scss',
'dist/css/mobile.css': 'src/scss/mobile.scss'
}
},
prod: {
options: {
sourceMap: false,
outputStyle: 'compressed'
},
files: {
'dist/css/main.min.css': 'src/scss/main.scss',
'dist/css/admin.min.css': 'src/scss/admin.scss',
'dist/css/mobile.min.css': 'src/scss/mobile.scss'
}
}
},
autoprefixer: {
options: {
browsers: ['last 3 versions', 'ie 8', 'ie 9']
},
dev: {
src: 'dist/css/*.css'
},
prod: {
src: 'dist/css/*.min.css'
}
},
cssmin: {
options: {
advanced: false,
keepSpecialComments: 0
},
prod: {
files: [{
expand: true,
cwd: 'dist/css/',
src: ['*.css', '!*.min.css'],
dest: 'dist/css/',
ext: '.min.css'
}]
}
}
Her Yerde Geçici Dosyalar: Grunt’ın task-based yaklaşımı her adımın diske yazması demekti. Tipik bir build onlarca geçici dosya yaratabilir, bu da yavaş ve debug etmesi zor olmasına yol açıyordu.
Incremental Processing Yok: Bir dosyayı değiştir, her şeyi yeniden build et. Bu, projeler yüzlerce dosyaya ulaştığında sürdürülebilir değildi.
Gulp ve Streaming Build’e Geçiş (2013-2016)#
Eric Schoffstall, Gulp’ı configuration dosyaları yerine JavaScript code’u üzerine kurdu; veriyi memory içi stream’lerle taşıyıp Grunt’ın her adımda yazdığı geçici dosyaları atladı.
Stream Pipeline’ı#
const gulp = require('gulp');
const sass = require('gulp-sass');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const autoprefixer = require('gulp-autoprefixer');
gulp.task('styles', function() {
return gulp.src('src/scss/**/*.scss')
.pipe(sass())
.pipe(autoprefixer('last 3 versions'))
.pipe(gulp.dest('dist/css'));
});
gulp.task('scripts', function() {
return gulp.src('src/js/**/*.js')
.pipe(concat('app.js'))
.pipe(uglify())
.pipe(gulp.dest('dist/js'));
});
gulp.task('watch', function() {
gulp.watch('src/scss/**/*.scss', ['styles']);
gulp.watch('src/js/**/*.js', ['scripts']);
});
gulp.task('default', ['styles', 'scripts', 'watch']);
Geçici dosya yok, her şey memory’de oluyordu; bu da build’i baştan sona hızlandırıyordu. Pipe metaforu developer’ların data transformation hakkında düşünme şekliyle uyuşuyordu, stream’ler de hataları handle etmeyi ve report etmeyi kolaylaştırdı.
Gulp Neden Kazandı (Bir Süreliğine)#
Gulp, configuration’dan çok programming gibi hissettirdiği için büyük kabul gördü. Developer’lar karmaşık build senaryolarını handle etmek için JavaScript mantığını kullanabiliyorlardı:
gulp.task('scripts', function() {
const isProduction = process.env.NODE_ENV === 'production';
let stream = gulp.src('src/js/**/*.js')
.pipe(concat('app.js'));
if (isProduction) {
stream = stream.pipe(uglify());
}
return stream.pipe(gulp.dest('dist/js'));
});
Takımları ikna eden kazanç watch modu oldu. Stream’ler her Grunt task’inin bağlı olduğu ara disk yazmalarını atladığı için, incremental rebuild siz pencere değiştirirken bitiyordu.
Module Sorunu#
Hem Grunt hem Gulp build automation problemini çözdü, ama daha derin bir sorunu ortaya çıkardılar: JavaScript’in native module sistemi yoktu. Dosyaları concatenate edebiliyordunuz, ama dependency’leri hala manuel yönetmek zorundaydınız.
2013’ten bu yaygın pattern’i düşünün:
// utils.js'de
var Utils = {
formatDate: function(date) { /* ... */ },
parseJSON: function(str) { /* ... */ }
};
// models.js'de (utils.js'e bağımlı)
var User = {
create: function(data) {
var parsed = Utils.parseJSON(data);
// ...
}
};
// views.js'de (models.js ve utils.js'e bağımlı)
var UserView = {
render: function(user) {
var date = Utils.formatDate(user.createdAt);
// ...
}
};
Dependency sırası hala manualdi:
<script src="js/utils.js"></script>
<script src="js/models.js"></script>
<script src="js/views.js"></script>
<script src="js/app.js"></script>
Sırayı değiştirmek uygulamayı bozabilirdi ve bu problem, uygulamalar büyüdükçe çok daha kötüye gidecekti.
Modül Sistemi Savaşları (2009-2014)#
Grunt ve Gulp build automation’ı çözerken, paralel bir evrim yaşanıyordu: JavaScript sonunda module sistemler alıyordu. Problem şuydu ki üç farklı yaklaşım ortaya çıktı, her birinin farklı felsefeleri vardı.
CommonJS ve Synchronous Modül Yükleme#
Node.js tarafından popülerleştirilen CommonJS, synchronous require() çağrıları kullanıyordu:
// math.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
module.exports = {
add: add,
multiply: multiply
};
// app.js
var math = require('./math');
console.log(math.add(1, 2)); // 3
Bu, dosyaların local olduğu Node.js için mükemmel çalışıyordu, ama browser’lar UI’yi bloke etmeden module’leri synchronous yükleyemiyordu.
Asynchronous Module Definition (AMD)#
RequireJS, asynchronous loading’i handle etmek için AMD’yi tanıttı:
// math.js
define(function() {
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
return {
add: add,
multiply: multiply
};
});
// app.js
require(['./math'], function(math) {
console.log(math.add(1, 2)); // 3
});
AMD browser’daki yükleme sorununu çözdü. Bedeli, birçok developer’ın doğal bulmadığı verbose ve callback-heavy bir kod oldu.
UMD: Universal Module Definition#
UMD her yerde çalışan module’ler yaratmaya çalıştı:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['exports'], factory);
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// CommonJS
factory(exports);
} else {
// Browser globals
factory((root.myModule = {}));
}
}(typeof self !== 'undefined' ? self : this, function (exports) {
function add(a, b) {
return a + b;
}
exports.add = add;
}));
UMD her yerde çalışıyordu. Elle yazan azdı: verbosity yüzünden genellikle tool’lar tarafından generate ediliyordu.
Pratikteki Kaos#
Pratikte, çoğu proje module formatlarının bir karışımıyla sonuçlanıyordu. Tipik bir proje şunlara sahip olabilirdi:
- AMD kullanan third-party kütüphaneler (RequireJS ekosistemi)
- CommonJS kullanan server-side kod (Node.js modülleri)
- Global değişkenler kullanan legacy kod
- Takımın “standart” olarak karar verdiği her neyse onu kullanmaya çalışan yeni kod
2013’teki projeler genellikle uygulama kodu için RequireJS, global $ bekleyen jQuery plugin’leri ve build script’leri için Node.js modülleri karışımı kullanıyordu. Bu üçünü uzlaştıran shim ve path configuration’ı o kadar uzuyordu ki tablonun tamamını kafasında tutan pek kimse kalmıyordu.
Browserify Node.js Modüllerini Browser’a Taşıyor (2011-2016)#
Browserify yeni bir module formatı yaratmaktan kaçındı: James Halliday (substack), CommonJS’in kendisini browser’da çalışır hale getirdi.
Browserify Akışı#
# Node.js gibi dependency'leri yükle
npm install underscore jquery
# Node.js gibi kod yaz
# app.js
var _ = require('underscore');
var $ = require('jquery');
$('#app').html(_.template('<h1>Hello <%= name %>!</h1>')({ name: 'World' }));
# Browser için bundle et
browserify app.js -o bundle.js
Bu, AMD-CommonJS-UMD kararını tek bir formata indirdi, browser’a binlerce mevcut npm modülüne erişim kazandırdı ve developer’ların Node.js’ten zaten bildiği syntax’ı yazmasını sağladı. Transform pipeline’ı da plugin’lerin bundling sırasında kodu modify etmesine izin verdi.
Transform’larla Gelen İlk Processing Pipeline#
Browserify’ın transform sistemi modern webpack loader’larının öncüsüydü:
# ES6'yı ES5'e transform et
browserify app.js -t babelify -o bundle.js
# CoffeeScript'i transform et
browserify app.coffee -t coffeeify -o bundle.js
# Template'leri transform et
browserify app.js -t hbsfy -o bundle.js
Sofistike processing pipeline’ları yaratmak için transform’ları chain edebiliyordunuz:
browserify app.js \
-t [ babelify --presets es2015 ] \
-t envify \
-t uglifyify \
-o bundle.js
npm + Browserify Ekosistemi#
İlk kez, frontend development backend development ile aynı package ekosistemini kullanabiliyordu. Date manipulation mı istiyorsun? npm install moment. HTTP request’leri mi lazım? npm install axios.
Bu erişim bir geri besleme döngüsü yarattı: daha fazla package hem Node.js’te hem browser’larda çalışan “isomorphic” hale geldi, bu da frontend projelerin olgunlaşmış server-side kütüphaneleri kullanmasını sağladı ve JavaScript ekosistemini npm etrafında birleştirdi.
Bundle Boyutu ve Asset Sorunları#
Uygulamalar büyüdükçe, Browserify’ın basitliği bir limitasyon haline geldi. Bir projeden sadece tek bir fonksiyon kullanılsa bile tüm modülü include ediyordu; sadece _.map çağırmak için full Lodash kütüphanesini yüklemek büyük bundle’larla sonuçlanıyordu. Her şey tek bir bundle.js dosyasına gidiyordu ve code splitting yoktu, bu yüzden büyük uygulamalar multi-megabyte dosyalarla sonuçlanıyordu. Browserify sadece JavaScript’i handle ediyordu; CSS, resimler ve diğer asset’ler hala ayrı tooling’e ihtiyaç duyuyordu, büyük projelerde bundle işlemi de incremental compilation olmadan dakikalar sürebiliyordu.
Webpack Her Şeyi Modül Grafiğine Çeviriyor (2012-Şimdi)#
webpack’in temel fikri her şeyi module olarak ele almaktı: CSS, resimler ve fontlar da JavaScript’le aynı dependency graph’a giriyordu. Tobias Koppers tool’u bu tek fikir üzerine kurdu.
Her Şey Module#
// JavaScript modülleri
import utils from './utils.js';
// CSS modülleri
import './styles.css';
// Image modülleri
import logo from './logo.png';
// JSON modülleri
import config from './config.json';
// Hatta HTML template'leri
import template from './template.html';
Bu yaklaşım birden fazla problemi aynı anda çözdü:
- Dependency tracking: webpack hangi dosyaların gerekli olduğunu tam olarak biliyordu
- Dead code elimination: Kullanılmayan dosyalar bundle’a dahil edilmiyordu
- Cache busting: File hash’leri otomatik generate ediliyordu
- Asset optimization: Resimler otomatik optimize edilebilir, inline edilebilir veya convert edilebiliyordu
Loader Sistemi#
webpack’in loader sistemi Browserify transform’larından ilham aldı ama çok daha güçlüydü:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
]
}
};
Code Splitting ve Lazy Loading#
webpack dynamic import’lara dayalı otomatik code splitting tanıttı:
// Dynamic import ayrı bundle yaratır
import('./heavy-feature.js').then(module => {
module.initialize();
});
// Multiple entry point'ler multiple bundle yaratır
module.exports = {
entry: {
app: './src/app.js',
admin: './src/admin.js'
}
};
Bu, Browserify’ın handle edemediği bundle size problemini çözdü. Uygulamalar minimal kodu upfront yükleyebilir ve ek feature’ları on demand fetch edebilirdi.
Hot Module Replacement#
webpack-dev-server Hot Module Replacement (HMR) tanıttı, ancak başlangıçta deneysel bir özellikti ve dikkatli konfigürasyon gerektiriyordu:
// Bu dosyadaki değişiklikler browser'ı refresh etmeden güncelliyor
if (module.hot) {
module.hot.accept('./component.js', function() {
// Component'i yerinde güncelle
updateComponent();
});
}
CSS değişiklikleri page refresh olmadan anındaydı, JavaScript değişiklikleri uygulama state’ini sıfırlamak yerine korudu, source map’ler debugging’i kolaylaştırdı ve incremental compilation development build’lerini hızlı tuttu.
Configuration Karmaşıklığı#
webpack’in gücü karmaşıklık pahasına geliyordu. webpack 2 dönemine ait tipik bir config:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
app: './src/app.js',
vendor: ['react', 'react-dom', 'lodash']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash].js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
},
{
test: /\.(png|svg|jpg|gif)$/,
use: {
loader: 'file-loader',
options: {
name: '[path][name].[hash].[ext]'
}
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new ExtractTextPlugin('[name].[contenthash].css'),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})
],
resolve: {
modules: [
path.resolve(__dirname, 'src'),
'node_modules'
]
}
};
Bu configuration gerekliydi ama korkutucu. Birçok developer karmaşıklığı nedeniyle webpack’ten kaçınıyordu, bu da Create React App gibi “zero-config” tool’ların yükselişine yol açtı.
Ekosistem Yakınsaması (2015-2018)#
2015’e kadar, frontend tooling ekosistemi birkaç temel prensip etrafında yakınsamıştı:
Evrensel Paket Yöneticisi Olarak npm#
Bower esasen ölmüştü. npm, hem frontend hem backend package’ları destekleyerek, nested dependency’leri düzgün handle ederek, daha iyi version resolution sağlayarak ve takımların zaten kullandığı build tool’larla integrate olarak package management savaşını kazanmıştı.
Standart Olarak ES6 Modülleri#
ES6 (ES2015) sonunda JavaScript’e native module sistemi verdi:
// math.js
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// app.js
import { add, multiply } from './math.js';
Bu, CommonJS’in temiz syntax’ını AMD’nin static analysis faydalarıyla sağladı.
Dönüştürme Katmanı Olarak Babel#
Babel, eski browser’larda modern JavaScript kullanmak için essential hale geldi:
// Modern kod yaz
const users = await fetch('/api/users').then(r => r.json());
const admins = users.filter(u => u.role === 'admin');
// Babel compatible kod'a transform eder
var users = fetch('/api/users').then(function(r) { return r.json(); });
var admins = users.filter(function(u) { return u.role === 'admin'; });
Build Standardı Olarak webpack#
Karmaşıklığına rağmen, webpack başka hiçbir tool’un çözemediği problemleri çözdüğü için de facto standart haline geldi: CommonJS, AMD ve ES6’yı kapsayan tek bir module sistemi, asset management, code splitting, hot module replacement ve production optimizasyonlarının hepsi tek bir tool’da.
Kalan Sorunlar#
2016’ya kadar, modern frontend tooling stack kurulmuştu, ama birkaç acı noktası kalıyordu. Yeni bir proje kurmak; bundling için webpack, transpilation için Babel, linting için ESLint, testing için Jest ve CSS processing için PostCSS öğrenmek demekti, her biri kendi configuration dosyası ve zihniyetiyle. Tipik bir projede 6-8 configuration dosyası ve yüzlerce satır setup kodu birikiyordu, üstelik bu tool’lar birbiriyle uyumlu çalışacak şekilde tasarlanmamıştı: birindeki bir değişiklik, diğerinin varsayımlarını habersizce bozabilirdi.
Performansın da bir bedeli vardı. Büyük webpack build’leri 30+ saniye sürebiliyordu; hot reloading development sırasında yardımcı oluyordu, ama production build’leri acı verici derecede yavaş kalıyordu. Bundle size’ı optimize etmek webpack internal’larını derinlemesine bilmeyi gerektiriyordu: tree shaking, code splitting ve chunk optimization gibi konseptler karmaşık ve kötü dokümante edilmişti.
Bu problemler bir sonraki inovasyon dalgası için sahneyi hazırladı: 2017-2020’de ortaya çıkacak zero-config tool’lar, performance-odaklı bundler’lar ve framework-integrated tooling.
Sonrasının Temeli#
Tool’lar güçlüydü ama karmaşıktı. Bu takas ancak proje webpack’in gerçekten sunduğu şeye ihtiyaç duyduğunda anlamlıydı: tek module formatı, asset handling ve code splitting’in tek geçişte çözülmesi. Üç script ve bir stylesheet’ten ibaret bir tanıtım sitesi, webpack varsayılan cevap haline geldikten yıllar sonra bile Gulp pipeline’ıyla daha rahat ediyordu.
Bu serinin bir sonraki bölümü Parcel, Vite ve esbuild’i, Next.js ile Vue CLI’ın opinionated alternatiflerini ve native ES modülleri ile HTTP/2’nin bundling varsayımlarını nasıl değiştirdiğini ele alıyor.
Kaynaklar#
- webpack - Kavramlar (yeni sekmede açılır) - Giriş noktası, çıktı, yükleyiciler, eklentiler ve mod dahil modül paketleyicisinin temel kavramlarını kapsayan resmi webpack dokümantasyonu
- Babel - Babel Nedir? (yeni sekmede açılır) - ES2015+ sözdizimini tüm tarayıcılarda kullanılabilir hale getiren JavaScript derleyicisini açıklayan resmi Babel dokümantasyonu
- Grunt: JavaScript Görev Çalıştırıcı (yeni sekmede açılır) - Gruntfile yapılandırması, görev bileşimi ve eklenti ekosistemini kapsayan resmi Grunt dokümantasyonu
- gulp.js - Hızlı Başlangıç (yeni sekmede açılır) - Yapılandırma yerine kod yaklaşımıyla akış tabanlı görev otomasyonunu tanıtan resmi Gulp dokümantasyonu
- Rollup - Giriş (yeni sekmede açılır) - Kütüphane yazarları için tree-shaking’i öncüleyen ES modül paketleyicisini açıklayan resmi Rollup dokümantasyonu
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.
Bu serideki tüm yazılar
İlgili yazılar
Webpack'ten önce dosyaları Grunt ile birleştirir, jQuery spagettisiyle boğuşurduk. Frontend tooling'in manuel dosya yönetiminden build sistemlerine evrimi.
javascript · build-tools · technical-debt +2
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.
javascript · build-tools · best-practices +6
Nub ve Vite+, 2026'nın oxc tabanlı iki Rust araç zinciri; rakip gibi görünüp öyle değiller. Hangi ikilinin hangi repoda yeri olduğuna dair net bir kural.
javascript · typescript · vite +3
Production zaman hatalarının kökleri, Moment.js'den Day.js ve date-fns'e geçiş ve her yerde UTC kullanıp dönüşümü görüntüleme sınırına bırakma.
nodejs · javascript · productivity +3
esbuild, SWC ve Vite gibi native tool'ların webpack'in hız sorunlarını nasıl çözdüğü: build süreleri saniyelerden milisaniyelere böyle indi.
esbuild · build-tools · vite +4