Budowanie tej strony: Podróż inżyniera cloud-native z powrotem do full-stacku
Po 6 latach koncentracji na backendzie i systemach cloud-native, odbudowałem swoje portfolio przy użyciu Next.js, TypeScript i Firebase. Dlaczego wybrałem ten stack i czego nauczyłem się wracając do developmentu full-stack?
LUSK
Opublikowano 24 kwietnia 2026
Budowanie tej strony: Podróż inżyniera cloud-native z powrotem do full-stacku

Przez ostatnie sześć lat żyłem w świecie backendu i cloud-native. Kubernetes, mikroserwisy, Terraform, architektura event-driven — to były moje codzienne narzędzia. Ale po drodze zdałem sobie z czegoś sprawę: straciłem kontakt z perspektywą full-stack.
Dlatego postanowiłem zbudować tę stronę od zera. Nie tylko jako portfolio, ale jako przemyślane ćwiczenie z przywracania umiejętności frontendowych przy jednoczesnym zastosowaniu backendowej dyscypliny w świecie frontendu.
To historia o tym, dlaczego wybrałem ten stack, jak podchodziłem do architektury i czego nauczyłem się wracając do full-stacku po pół dekady spędzonej w okopach backendu.
---
Dlaczego w ogóle to budować?
Jako inżynier cloud-native mógłbym użyć generatora stron statycznych, platformy no-code lub nawet gotowego szablonu. Ale to nie służyłoby mojemu celowi.
Musiałem:
- Odświeżyć umiejętności frontendowe — React, TypeScript, nowoczesny CSS i optymalizacja wydajności są niezbędne dla klientów B2B, którzy oczekują kompetencji full-stack.
- Zademonstrować myślenie architektoniczne — Pokazać, że podchodzę do frontendu z tą samą rygorystycznością co do backendu: wydajność, skalowalność, łatwość utrzymania (maintainability).
- Mieć pełną kontrolę nad całym stackiem — Od infrastruktury (Firebase Hosting) przez wdrożenie (GitHub Actions) po sam kod.
To nie jest tylko strona — to żywy dowód na to, że inżynier cloud-native może dostarczać dopracowane, wydajne doświadczenia frontendowe bez poświęcania doskonałości backendowej.
---
Tech Stack: Wybór narzędzi z intencją
Każda decyzja technologiczna była przemyślana. Oto uzasadnienie:
Next.js 16 (App Router)
Dlaczego: Domyślne Server Components, możliwość statycznego eksportu, doskonała obsługa TypeScript i wbudowane optymalizacje.
App Router zmienia zasady gry. Domyślnie komponenty są komponentami serwerowymi (Server Components) — brak nawadniania (hydration) JavaScriptem, chyba że jest to wyraźnie wymagane. Idealnie wpisuje się to w filozofię "najpierw wydajność":
1// Server Component — zero JavaScriptu po stronie klienta2export default async function Page() {3 const posts = await getAllPosts();4 return <BlogGrid posts={posts} />;5}67// Client Component — tylko gdy potrzebna jest interaktywność8'use client';9export default function InteractiveForm() {10 const [state, setState] = useState();11 // ...12}
Kluczowe korzyści:
- Automatyczne dzielenie kodu (code splitting).
- Renderowanie po stronie serwera z możliwością pełnego statycznego eksportu (`output: 'export'`).
- Route Handlers dla endpointów API (obsługa formularza kontaktowego).
- Wbudowana obsługa i18n poprzez `next-intl`.
---
TypeScript (Strict Mode)
Dlaczego: Po latach pracy z Go i TypeScriptem na backendzie, nie wyobrażam sobie pisania nietypowanego JavaScriptu. Bezpieczeństwo typów zapobiega całym klasom błędów.
Projekt wykorzystuje ścisłą konfigurację TypeScript:
- `strict: true` — brak niejawnego typu `any`.
- `noImplicitReturns` — wszystkie ścieżki kodu muszą zwracać wartość.
- `exactOptionalPropertyTypes` — bardziej rygorystyczne typowanie opcjonalnych właściwości obiektów.
Przykład z loadera postów na blogu:
1export interface PostMeta {2 slug: string;3 title: string;4 date: string;5 description: string;6 author: string;7 tags: string[];8 category: string;9 image: string;10 readingTime: string;11 locale: string;12}
Typy służą jako dokumentacja i zapobiegają błędom, zanim te w ogóle się pojawią. W kontekście B2B jest to nienegocjowalne.
---
Tailwind CSS v4
Dlaczego: CSS typu utility-first bez narzutu konfiguracyjnego. Wersja v4 całkowicie wyeliminowała potrzebę posiadania pliku `tailwind.config.ts` — konfiguracja znajduje się bezpośrednio w CSS poprzez bloki `@theme`.
System projektowania wykorzystuje tokeny inspirowane Material Design 3, zdefiniowane w `globals.css`:
1@theme {2 /* Kolory */3 --color-background: #121318;4 --color-on-surface: #e8e8e8;5 --color-primary-container: #1e2130;6 --color-on-primary-container: #adc6ff;7 --color-secondary: #55d7ed;89 /* Typografia */10 --font-sans: 'Inter', 'Space Grotesk', sans-serif;1112 /* Zaokrąglenia */13 --radius-sm: 0.375rem;14 --radius-md: 0.5rem;15}
Takie podejście trzyma style blisko kodu, zachowując jednocześnie spójność. Responsywny design "mobile-first" staje się trywialny:
1<div className="py-24 px-8 md:px-12 lg:px-24 max-w-7xl mx-auto">2 {/* Mobile: 2rem padding, Tablet: 3rem, Desktop: 6rem */}3</div>
---
Firebase (Hosting + Cloud Functions)
Dlaczego: Choć intensywnie korzystałem z GCP i Kubernetes, dla strony-portfolio Firebase jest właściwym narzędziem do zadania.
Hosting: Statyczny eksport oznacza, że mogę wdrożyć stronę gdziekolwiek (S3, Netlify, Vercel). Wybrałem Firebase, ponieważ:
- Darmowy poziom (tier) jest niezwykle hojny.
- Globalny CDN od razu po wyjęciu z pudełka.
- Proste CLI (`firebase deploy`).
- Zintegrowane Cloud Functions dla potrzeb backendowych.
Cloud Functions: Handler formularza kontaktowego działa jako funkcja Node.js 24 z ograniczaniem liczby żądań (rate limiting) i walidacją:
1export const contactSubmit = onRequest(2 { region: 'us-central1' },3 corsMiddleware,4 rateLimiter,5 jsonParser,6 validateContactSchema,7 async (req, res) => {8 // Przetwarzanie formularza9 // Wysyłanie emaila, zapis w Firestore10 // Zwrócenie sukcesu11 }12);
Pokazuje to, że nawet "proste" portfolio wymaga właściwej architektury backendowej: walidacji, rate limitingu i solidnej obsługi CORS.
---
Framer Motion
Dlaczego: Animacja powinna wzbogacać UX, a nie rozpraszać. Framer Motion zapewnia wydajne, dostępne animacje przy minimalnym wpływie na rozmiar paczki (bundle size).
Pokazywanie elementów przy przewijaniu (scroll-triggered reveals) wykorzystuje komponent `Reveal`:
1<Reveal>2 <section className="opacity-0 translate-y-8">3 {/* Zawartość */}4 </section>5</Reveal>
Komponent automatycznie respektuje ustawienie `prefers-reduced-motion` — dostępność nie jest tutaj czymś dodanym na samym końcu.
---
Architektura: Myślenie backendowe na frontendzie
Największą zmianą w porównaniu do czysto backendowej pracy było zastosowanie zasad inżynierii oprogramowania w architekturze frontendu.
Projektowanie komponentów: Jedna odpowiedzialność (Single Responsibility)
Każdy komponent robi jedną rzecz dobrze:
- `Navbar.tsx` — Tylko nawigacja.
- `Hero.tsx` — Tylko sekcja Hero.
- `BlogCard.tsx` — Podgląd pojedynczego wpisu.
- `BlogGrid.tsx` — Układ siatki wpisów.
Żadnych monolitycznych komponentów. Ułatwia to testowanie, utrzymanie i ponowne użycie kodu.
Server vs. Client Components
Next.js App Router zmusza do zastanowienia się, co naprawdę wymaga JavaScriptu po stronie klienta:
Server Components (Domyślnie):
- Pobieranie danych.
- Statyczna zawartość.
- Brak wymaganej interaktywności.
Client Components (Jawnie):
- Formularze (strona kontaktu).
- Animacje (Framer Motion).
- Przełącznik języka (hooki `next-intl`).
Rezultat: Minimalna paczka JavaScript. Strona główna nawadnia (hydrates) tylko to, co jest absolutnie niezbędne.
i18n: next-intl
Internacjonalizacja została wbudowana od samego początku, a nie doklejona później. Struktura języków (locales):
1app/[locale]/2 layout.tsx # Główny układ z dostawcą wiadomości3 page.tsx # Strona główna4 blog/5 page.tsx # Lista wpisów6 [slug]/page.tsx # Pojedynczy wpis
Wiadomości są ładowane po stronie serwera:
1export default async function Page({ params }: { params: Promise<{ locale: string }> }) {2 const { locale } = await params;3 const messages = await getMessages({ locale });4 // Przekazanie do komponentów5}
Brak dostawców kontekstu (context providers) zaśmiecających drzewo komponentów. Czysto, bezpieczne typowanie i wydajnie.
Testowanie z Vitest
Przeniosłem backendową rygorystyczność na frontend, wymuszając solidną metodologię testowania. Projekt wykorzystuje Vitest wraz z React Testing Library:
1// tests/BlogPost.test.tsx2describe('BlogPost', () => {3 it('renders title and content correctly', () => {4 render(<BlogPost post={mockPost} />);5 expect(screen.getByText('Test Post')).toBeInTheDocument();6 });7});
Walidacja renderowania i funkcjonalności zapewnia stabilność przy aktualizacjach i pokazuje, że kod frontendowy zasługuje na ten sam standard QA, co wdrożenia backendowe.
Techniczne SEO jako problem inżynieryjny
Osiągnięcie wyników Lighthouse Core Web Vitals to tylko połowa sukcesu. Traktując SEO jako zadanie inżynieryjne, zaimplementowałem:
- Automatyczne mapy witryn XML (`next-sitemap`).
- Schematy JSON-LD przy użyciu modułu `StructuredData`.
- Automatyczne kanały RSS (`app/[locale]/rss/page.tsx`).
To ustrukturyzowane podejście gwarantuje, że Google poprawnie modeluje dokładną wartość biznesową i powiązania portfolio w całej witrynie.
---
Statyczny eksport: Zwycięzca wydajności
`next.config.ts` wykorzystuje `output: 'export'`:
1const nextConfig: NextConfig = {2 output: 'export',3 // Tylko statyczny HTML — serwer Node.js nie jest potrzebny4};
Generuje to czyste pliki statyczne w `front/out/`:
- `index.html` — Strona główna.
- `blog/index.html` — Lista wpisów.
- `blog/[slug]/index.html` — Pojedyncze wpisy.
- `og/blog/[slug].svg` — Automatycznie generowane obrazy OG.
Możliwość wdrożenia gdziekolwiek. Brak serwera w czasie rzeczywistym (runtime). Wyniki Lighthouse konsekwentnie powyżej 90 punktów.

Aby zbudować i podejrzeć stronę lokalnie:
1cd front && npm run build2npx serve out -p 5000
---
Silnik bloga: Markdown + gray-matter
Brak CMS, brak bazy danych. Tylko pliki Markdown:
1content/posts/2 2026-04-15-cloud-native-best-practices/3 index.md # Angielski4 index.pl.md # Polski
Metadane (frontmatter) definiują właściwości:
1---2title: "Building Scalable Cloud-Native Applications"3date: "2026-04-15"4description: "Learn best practices..."5tags: ["kubernetes", "microservices"]6category: "Tutorial"7readingTime: "12 min"8---
Loader w `lib/posts.ts`:
1. Odczytuje system plików w czasie budowania.
2. Parsuje metadane za pomocą `gray-matter`.
3. Konwertuje Markdown na HTML za pomocą `remark`.
4. Zwraca otypowane obiekty Post.
Podczas `next build`, funkcja `generateStaticParams` wstępnie renderuje wszystkie wpisy:
1export function generateStaticParams() {2 const slugs = getPostSlugs('en').concat(getPostSlugs('pl'));3 return slugs.map(slug => ({ slug }));4}
Brak zapytań do bazy danych w czasie rzeczywistym. Wszystko jest statyczne. To ostateczna optymalizacja wydajności.
---
CI/CD: GitHub Actions + Firebase
Wdrożenie jest w pełni zautomatyzowane dzięki dwóm przepływom (workflows) GitHub Actions:
- `firebase-hosting-merge.yml` — Wdraża na produkcję przy scalaniu do gałęzi `main`.
- `firebase-hosting-pull-request.yml` — Wdraża adresy URL podglądu (preview URLs) dla Pull Requestów.
Każdy przepływ:
- Pobiera kod (Checkout).
- Ustawia Node.js 24.x.
- Instaluje zależności (`npm ci`).
- Buduje frontend (`npm run build`).
- Buduje Cloud Functions (`npm --prefix functions run build`).
- Wdraża do Firebase.
Brak ręcznych kroków. Git jest źródłem prawdy. Scalenie do `main` = publikacja strony.
---
Lekcje wyciągnięte z doświadczenia: Powrót do frontendu
Po 6 latach pracy z backendem i rozwiązaniami cloud-native, oto co odświeżyłem (i ponownie doceniłem):
1. CSS nadal jest trudny
Potrafię z pewnością siebie projektować systemy rozproszone, ale wyśrodkowanie diva wciąż wymaga zaglądania na Stack Overflow. Tailwind pomaga, ale zmiana mentalna z "infrastruktura jako kod" na "stylowanie jako klasy pomocnicze" była realna.
Wniosek: Umiejętności frontendowe bez praktyki ulegają degradacji. Nawet jako doświadczony inżynier musisz trzymać rękę na pulsie.
2. Ekosystem JavaScript zmienia się błyskawicznie
Next.js 16, React 19, Tailwind v4 — wszystko zmieniło się od czasu, gdy ostatni raz profesjonalnie budowałem aplikację frontendową (era Reacta 16). Poprawa DX (Developer Experience) jest ogromna, ale krzywa uczenia się jest stroma, jeśli nie używasz tego na co dzień.
Wniosek: Inżynierowie backendu powinni od czasu do czasu budować projekty frontendowe, aby pozostać w formie. Platforma webowa ewoluuje szybko.
3. Wydajność jest wielowymiarowa
Wydajność backendu = opóźnienia (latency), przepustowość (throughput), koszt. Wydajność frontendu = wyniki Lighthouse, Core Web Vitals, SEO, dostępność, rozmiar paczki, koszt nawadniania, czasy renderowania, ...
To inny rodzaj złożoności, ale niemniej wymagający.
Wniosek: Wydajność frontendu wymaga obsesyjnej dbałości o szczegóły. Każdy komponent, każdy obrazek i każdy tag skryptu ma znaczenie.
4. Server Components zmieniają wszystko
Zespół Reacta trafił w dziesiątkę. Poprzez domyślne przeniesienie renderowania na serwer, wymusili przemyślenie architektury frontendu na nowo. Rezultat: mniej JavaScriptu, lepsza wydajność i czystszy kod.
Wniosek: Server Components to nie tylko optymalizacja — to zmiana paradygmatu. Korzystaj z nich jeśli pasuje to do kontekstu.
5. TypeScript na frontendzie to absolutna podstawa (Table Stakes)
W środowisku backendowym typy są rzeczą oczywistą. Na frontendzie wciąż wiele projektów korzysta z czystego JavaScriptu. Uważam to błąd. TypeScript wyłapuje błędy wcześnie, umożliwia refaktoryzację i służy jako dokumentacja.
Wniosek: Nigdy nie zaczynaj projektu frontendowego bez TypeScriptu. Nawet jeśli to tylko portfolio.
---
Co to oznacza dla klientów B2B
Zbudowałem tę stronę nie tylko jako portfolio, ale jako demonstrację mojego podejścia do projektów klientów:
- Architektoniczna rygorystyczność zastosowana do frontendu (Server Components, renderowanie statyczne, bezpieczeństwo typów).
- Myślenie infrastrukturalne (CDN, cache'owanie, optymalizacja wydajności, SEO).
- Automatyzacja (CI/CD, generowanie obrazów OG, statyczne budowanie).
- Pełna odpowiedzialność (Full-stack ownership) — Mogę obsłużyć backend (Firebase Functions) i frontend w ramach tego samego kodu.
Kiedy klient B2B mnie zatrudnia, otrzymuje osobę, która rozumie cały system — od klastra Kubernetes po przeglądarkę użytkownika. Ten projekt udowadnia, że opanowując backend, nie zapomniałem o frontendzie.
---
Wnioski
Budowanie tej strony było w równej mierze odświeżeniem umiejętności, co deklaracją zasad. Pokazuje, że:
- Inżynierowie cloud-native mogą dostarczać wyjątkowe doświadczenia frontendowe.
- Nowoczesne frameworki (Next.js, Tailwind) pozwalają na osiągnięcie profesjonalnych rezultatów bez poświęcania inżynierskiej rygorystyczności.
- Najlepsze praktyki backendowe (bezpieczeństwo typów, automatyzacja, wydajność) mają równie silne zastosowanie na frontendzie.
Po 6 latach specjalizacji backendowej wracam do full-stacku — ale przez pryzmat cloud-native. Wszystko jest otypowane, zautomatyzowane, statyczne i domyślnie wydajne.
Tak buduję dla klientów i tak zbudowałem tą stronę.
---
Chcesz omówić, jak to podejście mogłoby przynieść korzyści Twojemu projektowi? [Skontaktuj się ze mną](/contact).