Manifest bez bundlera: produkcja bez narzędzi budowania
Poniższy tekst nie jest argumentem za porzuceniem narzędzi budowania. To pomiar tego, co się dzieje, gdy się je porzuci.
blakecrosley.com działa na FastAPI + Jinja2 + HTMX + Alpine.js + czystym CSS. Bez webpack. Bez Vite. Bez Rollup. Bez kompilatora TypeScript. Bez Babel. Bez PostCSS. Bez Tailwind. Bez package.json. Bez node_modules/. Strona serwuje 33 wpisy blogowe z 14 interaktywnymi komponentami JavaScript, 20 przewodników, tłumaczenia na dziewięć języków i uzyskuje wynik 100/100/100/100 w Lighthouse.
TL;DR
Społeczność HTMX nie ma problemów z entuzjazmem. Brakuje jej dowodów. Liczby przedstawione tutaj pochodzą z produkcyjnej strony z obszerną treścią, interaktywnymi funkcjami i internacjonalizacją — wszystko bez jednego narzędzia budowania. Kompromisy są uczciwe, a wniosek jest wąski: dla stron opartych na treści z jednym deweloperem lub małym zespołem narzędzia budowania rozwiązują problemy, których nie masz, jednocześnie tworząc problemy, które masz. Dla dużych zespołów ze współdzielonymi bibliotekami komponentów i pakietami systemu projektowego narzędzia budowania uzasadniają swoją złożoność. Granica jest wyraźniejsza, niż sugeruje dyskurs.
Stos technologiczny
Backend: FastAPI + Jinja2 (server-rendered HTML)
Frontend: HTMX + Alpine.js + Bootstrap 5 (CDN)
Styles: Plain CSS with custom properties
JavaScript: Vanilla JS, IIFE-scoped per component
Deployment: Railway (git push → live)
CDN: Cloudflare (caching, Workers, D1)
Bez transpilacji. Bez tree shaking. Bez hot module replacement. Bez source maps. JavaScript, który piszesz, jest JavaScriptem, który trafia do użytkownika.
Liczby
Oto rzeczywiste metryki, nie szacunki:
| Metryka | blakecrosley.com | Typowy projekt Next.js (szacunki autora)1 |
|---|---|---|
| Zależności | 18 pakietów Python | 300+ pakietów npm |
| Pliki konfiguracji budowania | 0 | 5-8 (next.config, tsconfig, postcss, tailwind, eslint, babel itp.) |
Rozmiar node_modules/ |
Nie istnieje | 150-400 MB |
| Czas instalacji | pip install -r requirements.txt: 8 sekund |
npm install: 30-90 sekund |
| Krok budowania | Brak | next build: 15-60 sekund |
| Pipeline wdrożenia | git push → na żywo w ~40 sekund |
git push → instalacja → budowanie → wdrożenie: 2-5 minut |
| Pliki CSS | 14 plików, 10 300 linii (czysty CSS) | Generowane z Tailwind/Sass, wynik różny |
| Pliki JS | 33 pliki, 10 061 linii (czytelne dla człowieka) | Zbundlowane, zminifikowane, podzielone na chunki: nieczytelne w produkcji |
| Wydajność Lighthouse | 100 | Różna (często 70-90 bez pracy optymalizacyjnej) |
18 pakietów Python obejmuje FastAPI, Jinja2, Pydantic, SQLAlchemy i 14 pozostałych. Żaden nie jest narzędziem budowania. Żaden nie jest kompilatorem. Żaden nie jest bundlerem.2
Czego się pozbywasz
Uczciwość wymaga wymienienia rzeczywistych kosztów. Są realne.
Brak TypeScript. Każdy plik .js w tym projekcie jest czystym JavaScriptem. Testy i analiza Claude Code wychwytują błędy typów, nie kompilator. Podejście to sprawdza się dla jednego dewelopera. Nie sprawdziłoby się w zespole 10 osób współdzielących interfejsy komponentów między modułami.
Brak Hot Module Replacement. Gdy zmieniam plik CSS, ręcznie odświeżam przeglądarkę. hx-boost z HTMX sprawia, że nawigacja jest wystarczająco szybka, by pełne odświeżenia były do zaakceptowania. W projekcie, w którym iteruję nad detalami wizualnymi co 30 sekund, HMR oszczędziłby znaczącą ilość czasu.
Brak Tree Shaking. Każdy bajt JavaScriptu, który piszę, trafia do przeglądarki. Nie mogę zaimportować pojedynczej funkcji z biblioteki narzędziowej bez wysyłania całego pliku. To ograniczenie wymusza dyscyplinę: małe, skupione pliki zamiast dużych modułów narzędziowych. 14 interaktywnych komponentów ma średnio 300-700 linii każdy, ponieważ muszą być samodzielne.3
Brak biblioteki komponentów z npm. Bez Radix, bez shadcn/ui, bez Headless UI. Każdy interaktywny element (symulacja boidów, wizualizator kodu Hamminga, symulator konsensusu) jest budowany ręcznie. Podejście to jest możliwe tylko dlatego, że interaktywne komponenty służą konkretnym celom dydaktycznym, a nie generycznym wzorcom UI.
Brak tokenów systemu projektowego z npm. Mój system projektowy istnieje wyłącznie w CSS custom properties. Nie mogę go zaimportować jako pakietu w innym projekcie. Dla systemu jednej strony to akceptowalne ograniczenie. Dla organizacji z wieloma produktami — nie.
Pięć kompromisów jest akceptowalnych dla strony opartej na treści z jednym deweloperem. Byłyby nie do przyjęcia dla produktu SaaS z 15-osobowym zespołem inżynierskim.
Co zyskujesz
Zero błędów budowania. Pipeline wdrożenia to git push. Żaden npm install nie zawiedzie z powodu konfliktu zależności peer. Żaden next build nie zawiedzie z powodu błędu TypeScript w pliku, którego nie ruszałem. Żaden PR Dependabota nie zaktualizuje tranzytywnej zależności i nie zepsuje budowania.4
Debugowanie przez View Source. JavaScript działający w przeglądarce to JavaScript, który napisałem. Bez potrzeby source maps. Bez mapowania ze skompilowanego wyjścia na oryginalne źródło. Gdy pojawia się błąd w produkcji, czytam bezpośrednio wdrożony plik.
Natychmiastowy start lokalny. uvicorn app.main:app --reload uruchamia się w mniej niż 2 sekundy. Bez npm run dev, które instaluje, kompiluje i bundluje, zanim pokaże stronę.
Zero szumu Dependabota. Brak package-lock.json oznacza brak cotygodniowych PR-ów aktualizujących semver, ansi-regex czy glob-parent — pakiety, których nigdy bezpośrednio nie importowałem, ale które tkwią trzy warstwy głęboko w drzewie zależności.
Odporność na przyszłość. Strona będzie działać za 10 lat. HTML to HTML. CSS to CSS. JavaScript to JavaScript. Nie ma migracji z Webpack 4 → 5, nie ma deprecjacji Create React App, nie ma migracji na Next.js App Router. Platforma to standard.5
HTMX jako architektura
Dyskurs wokół HTMX skupia się na składni: hx-get, hx-swap, hx-target. To błędne ujęcie. Kluczowy wgląd architektoniczny polega na tym, że HTML renderowany po stronie serwera jest API.
W tradycyjnym SPA:
Browser → fetch('/api/users') → JSON → React renders HTML → DOM update
Z HTMX:
Browser → GET /users (hx-get) → Server renders HTML fragment → DOM swap
Serwer zwraca finalną reprezentację. Bez zarządzania stanem po stronie klienta, bez serializacji/deserializacji, bez hydracji. Szablon Jinja2 jest komponentem. Endpoint FastAPI jest API. Jedna warstwa, nie trzy.6
Wzorzec ten bezpośrednio odpowiada zasadzie kumulującej się inżynierii: każdy element infrastruktury robi dokładnie jedną rzecz, a elementy komponują się bez wzajemnych zakłóceń. Szablon renderuje HTML. Route go zwraca. HTMX podmienia go w DOM. Żaden krok budowania nie koordynuje tych elementów, ponieważ koordynacja nie jest potrzebna.
Czysty CSS wystarczy
Mój system projektowy używa 10 tokenów kolorów, 13 kroków skali typograficznej i ośmiu wartości odstępów — wszystko jako CSS custom properties:
:root {
--color-bg-dark: #000000;
--color-text-primary: #ffffff;
--color-text-secondary: rgba(255,255,255,0.65);
--spacing-sm: 1rem;
--spacing-md: 1.5rem;
--font-size-lg: 1.25rem;
}
Bez kompilacji Sass. Bez konfiguracji Tailwind generującej klasy narzędziowe. Bez pluginów PostCSS transformujących niestandardową składnię. Przeglądarka odczytuje te wartości bezpośrednio.7
Estetyka piękna i brutalizmu tej strony (biel na absolutnej czerni z czterema poziomami przezroczystości) wyłania się z ograniczenia. Gdy nie można sięgnąć po paletę kolorów, typografia przejmuje hierarchię. Gdy nie można sięgnąć po cienie komponentów, pusta przestrzeń tworzy strukturę. Ograniczenie jest projektem.8
Podróż z CLS
Droga do idealnego Lighthouse ujawniła jeden realny koszt braku narzędzi budowania: ekstrakcja krytycznego CSS wymagała niestandardowego skryptu Python. W projekcie Next.js framework obsługuje to automatycznie.
Konkretny błąd: zapytanie medialne dla urządzeń mobilnych nadpisywało CSS custom property (--gutter: 48px → --gutter: 24px). Krytyczny CSS zawierał wartość desktopową, ale nie nadpisanie mobilne. Na urządzeniu mobilnym nagłówek renderował się z paddingiem 48px, a następnie przeskakiwał do 24px po załadowaniu pełnego arkusza stylów, generując CLS na poziomie 0,493.
Naprawa to 12 linii Python. Dochodzenie zajęło trzy godziny. Narzędzie budowania obsłużyłoby to automatycznie.
Uczciwe rozliczenie: narzędzia budowania automatyzują rzeczy, które można zrobić ręcznie, ale wersja ręczna kosztuje czas debugowania, gdy coś się psuje. Pytanie brzmi, czy koszt automatyzacji (złożoność, zależności, błędy budowania, ciągłe migracje) przewyższa koszt ręczny (okazjonalne sesje debugowania).
Dla tej strony koszt ręczny był niższy. Trzy lata, jeden błąd CLS, trzy godziny debugowania. Alternatywa (utrzymywanie pipeline budowania) pochłonęłaby więcej skumulowanego czasu na aktualizacje zależności, zmiany łamiące kompatybilność i konserwację konfiguracji.
Kiedy tego nie stosować
Podejście bez narzędzi budowania nie sprawdza się w przypadku:
Dużych zespołów. Wartość TypeScript rośnie proporcjonalnie do wielkości zespołu.9 Gdy 10 deweloperów współdzieli interfejsy komponentów, sprawdzanie typów w czasie kompilacji zapobiega błędom integracji, które testowanie w runtime wykrywa zbyt późno. Jeden deweloper utrzymuje cały system w głowie. Zespół nie może.
Pakietów systemu projektowego. Jeśli wiele produktów korzysta z systemu projektowego, musi on być pakietem npm z odpowiednim wersjonowaniem, tree shakingiem i pipeline budowania. CSS custom properties w jednym arkuszu stylów nie komponują się między repozytoriami.
Złożonego stanu po stronie klienta. Jeśli aplikacja wymaga rozbudowanego stanu klienta (interfejsy drag-and-drop, współpraca w czasie rzeczywistym, dane offline-first), framework taki jak React czy Svelte uzasadnia swoją złożoność. HTMX zastępuje stan klienta zapytaniami do serwera, co działa, dopóki opóźnienie nie ma znaczenia.
Bibliotek z ekosystemu npm. Jeśli potrzebujesz prymitywów Radix, Framer Motion czy TanStack Query, potrzebujesz pipeline budowania. Wszystkie trzy zakładają obecność bundlera. Używanie ich bez niego waha się od bolesnego do niemożliwego.
Granica jest prostsza, niż sugeruje dyskurs: jeśli aplikacja to głównie treść renderowana przez serwer, narzędzia budowania są narzutem. Jeśli aplikacja to głównie stan zarządzany przez klienta, narzędzia budowania są infrastrukturą.
Kluczowe wnioski
Dla deweloperów indywidualnych i małych zespołów:
-
Dowodem jest strona, nie argument. blakecrosley.com serwuje 33 wpisy, 14 interaktywnych komponentów, 20 przewodników i dziewięć języków bez żadnych narzędzi budowania i z perfekcyjnymi wynikami Lighthouse. Liczby są weryfikowalne.
-
Rzeczywisty koszt braku narzędzi budowania to okazjonalne debugowanie. Błąd CLS zajął trzy godziny naprawy. Narzędzie budowania obsłużyłoby go automatycznie. Przez trzy lata skumulowany czas debugowania był znacznie mniejszy niż skumulowany czas konserwacji, jakiego wymagałby pipeline budowania.
-
Ograniczenia tworzą projekt. Brak kolorów wymusił, by typografia niosła hierarchię. Brak narzędzi budowania wymusił prosty, samodzielny JavaScript. Najlepsze ograniczenia to te, które wybierasz, zanim ich potrzebujesz.
Dla liderów zespołów oceniających wybór stosu:
-
Narzędzia budowania rozwiązują problemy w skali zespołu. TypeScript, tree shaking i biblioteki komponentów uzasadniają swoją złożoność, gdy wielu deweloperów współdzieli interfejsy. Indywidualny deweloper budujący strony oparte na treści nie ma tych problemów.
-
Prawdziwym wkładem HTMX jest architektura. HTML renderowany po stronie serwera jako API eliminuje zarządzanie stanem klienta, serializację i hydrację. Składnia jest drugorzędna wobec tego wglądu.
Artykuł łączy sekcje Design i Praktyka inżynierska bloga. Decyzje projektowe opisano w Piękno i brutalizm, Systemy projektowe dla startupów i Skale typograficzne. Pomiary inżynierskie znajdują się w Perfekcyjny wynik Lighthouse i Kumulująca się inżynieria. Wpis o vibe codingu bada, gdzie ta filozofia ma zastosowanie w rozwoju wspomaganym przez AI.
-
Kolumna „Typowy projekt Next.js” odzwierciedla doświadczenia autora z ponad 5 projektami Next.js (2021-2024) oraz normy raportowane przez społeczność. Konkretne liczby (300+ pakietów, 150-400 MB node_modules) są spójne z powszechnie raportowanymi wartościami w społeczności Node.js. Poszczególne projekty różnią się znacząco. Liczby są szacunkami autora, nie niezależnie zweryfikowanymi benchmarkami. ↩
-
Pełna lista zależności na luty 2026: fastapi, uvicorn, starlette, pydantic, pydantic-settings, jinja2, markdown, pygments, beautifulsoup4, lxml, nh3, resend, python-dotenv, python-multipart, slowapi, httpx, sqlalchemy, analytics-941. Zero to narzędzia budowania. Zero to kompilatory. Zero to bundlery. ↩
-
Średni rozmiar komponentu (300-700 linii) zmierzony na podstawie 14 interaktywnych plików JS w
/static/js/na luty 2026. Rozmiary wahają się od 240 linii (signal-calculator.js) do 690 linii (boids-simulation.js). ↩ -
Na podstawie doświadczeń autora z utrzymywaniem projektów Next.js, ekosystem JavaScript generuje 15-25 PR-ów Dependabota miesięcznie dla aktywnego projektu, z czego większość aktualizuje tranzytywne zależności, których deweloper nigdy bezpośrednio nie importował. Wartość ta jest szacunkiem z bezpośredniej obserwacji, nie niezależnie zweryfikowanym benchmarkiem. ↩
-
Platforma webowa (HTML, CSS, JavaScript) utrzymuje wsteczną kompatybilność od 30 lat. Strona z 1996 roku nadal renderuje się w Chrome 2026. Tim Berners-Lee sformułował to jako zasadę projektową: „przeglądarka powinna być wstecznie kompatybilna, czyli powinna być w stanie odczytać wcześniejszą wersję języka”. Patrz w3.org/DesignIssues/Principles. ↩
-
Carson Gross, twórca HTMX, ujmuje to jako „hipermedia jako silnik stanu aplikacji” (HATEOAS). Patrz eseje htmx.org oraz książka Hypermedia Systems (2023) autorstwa Grossa, Stepinskiego i Cottera: hypermedia.systems. ↩
-
CSS Custom Properties (zmienne CSS) są obsługiwane w ponad 97% globalnych przeglądarek. Źródło: caniuse.com/css-variables. Do ich użycia nie jest potrzebny żaden krok kompilacji. ↩
-
Zasada „ograniczenie jako narzędzie projektowe” ma długą historię. Charles Eames: „Projekt w dużej mierze zależy od ograniczeń”. Ruch Dogma 95 w filmie udowodnił, że usunięcie narzędzi (brak sztucznego oświetlenia, brak postprodukcji) tworzyło bardziej szczerą narrację, nie mniej. Patrz en.wikipedia.org/wiki/Dogme_95. ↩
-
Badanie Stack Overflow Developer Survey 2024 wykazało, że TypeScript jest 4. najpopularniejszym językiem i najpopularniejszym nadzbiorem JavaScript, z adopcją rosnącą proporcjonalnie do wielkości zespołu. Patrz survey.stackoverflow.co/2024/. ↩