Tør du trykke deploy, når alle faner er lukket og ingen kigger?

Tør du trykke deploy, når alle faner er lukket og ingen kigger?

Det føles lidt som at tage på cykelferie uden værktøj. På parkeringspladsen ser alt fint ud, kæden kører, dækkene er pumpede, og du har vind i håret. 20 km senere står du i grøften med et fladt dæk og et meget teoretisk forhold til, hvad du kunne have gjort anderledes.

Sådan har jeg det med frontend-deploys.

Jeg har efterhånden haft nok “det virker på min maskine” øjeblikke til, at jeg er begyndt at skrive deploy-checklister til mig selv. Ikke fancy, bare små noter: “husk env vars”, “byg lokalt med samme kommando som CI”, “tjek 404 på deep links”.

I den her tekst gennemgår jeg den version, jeg faktisk bruger før jeg trykker deploy på små frontend-projekter. Den er stack-agnostisk, men jeg nævner Vercel, Netlify og GitHub Pages undervejs, fordi det er dem jeg selv ender på oftest.

Pre-flight: hvad jeg vil have styr på, inden jeg overhovedet bygger

Mit mønster er næsten altid det samme: jeg får lavet “den sidste feature”, alt kører i npm run dev, og fristelsen til bare at smide det på Vercel er stor. De gange jeg har gjort det, er det gået cirka lige så godt som at skifte bremseklodser på bilen dagen før syn uden at teste dem bagefter.

I dag tjekker jeg mindst de her ting, før jeg overhovedet tænker deployment:

  • Ingen hårdkodet localhost i koden
    Jeg søger efter localhost, 127.0.0.1 og porte som :3000, :5173.
    Før:
const API_URL = "http://localhost:3000/api";

Efter:

const API_URL = import.meta.env.VITE_API_URL; // Vite-eksempel
  • Node- og package-manager version
    Jeg skriver dem i .nvmrc eller engines i package.json, så CI ikke bygger med noget helt andet.
  • Build-scriptet
    CI skal køre npm run build (eller pnpm build) og ikke et eller andet jeg aldrig bruger lokalt.

Det lyder banalt, men de fleste “produktionen fejler, men dev kører fint” bugs jeg har haft, starter her.

Byg lokalt som CI gør: clean install og production build

Jeg har en dårlig vane med bare at køre npm run build i et miljø, hvor der har ligget node_modules i 100 år. Det er ikke det samme som det CI gør.

I dag tester jeg sådan her, før jeg stoler på noget:

rm -rf node_modules dist .turbo .next
rm package-lock.json # eller pnpm-lock.yaml / yarn.lock hvis du vil teste helt friskt
npm install
npm run build

Hvis buildet fejler her, så var det bare held, at det virkede før. Ikke stabilitet.

Bruger du Vite, Next, Nuxt eller lignende, så kig også lige ned i output-mappen (dist, .next, out):

  • Findes der faktisk en index.html eller 200.html?
  • Ligner assets-stierne noget, din host forstår? (ingen file:/// eller mærkelige base paths)

MDN har en fin intro til frontend-deployment, hvis du vil have det store billede: MDN deployment intro. Men start med at kunne bygge lokalt uden magi.

Miljøvariabler: det her går næsten altid galt første gang

Den mest klassiske fejl jeg ser (og laver) er env vars, der kun findes i min egen .env, men aldrig i Vercel/Netlify.

Min egen lille huskeliste:

Navngivning og scope

  • I Vite skal variabler til frontend starte med VITE_.
  • I Next skal de starte med NEXT_PUBLIC_, hvis de skal ud til browseren.
  • Filer som .env kommer ikke automatisk med op i deployment.

Så det, jeg gør, er:

  1. Opretter env vars i dashboardet
    Vercel: Project > Settings > Environment Variables
    Netlify: Site configuration > Environment variables
    GitHub Pages: typisk ikke til dynamic env, men du kan bruge GitHub Actions secrets til builds.
  2. Bygger igen i CI
    Trigger et nyt build efter jeg har sat env vars. Det hjælper ikke at ændre dem, hvis jeg ikke faktisk laver et nyt build.
  3. Logger værdier i build-step (uden hemmeligheder)
    En enkel console.log("API URL", import.meta.env.VITE_API_URL) i build-venlig kode (eller process.env.NAVN i Node-delen), så jeg kan se i logs, hvad der faktisk er sat.

Jeg har skrevet lidt mere generelt om env-vars problemer i en anden artikel: hvordan du undgår det kaos-API jeg selv startede med. Pointen er den samme her: antag ikke at din host kan gætte, hvad dine env vars skal være.

Routing: SPA, deep links og 404 som kun findes i produktion

Her er en fejl jeg har lavet mindst ti gange:

  1. Byg en SPA med client-side routing (React Router, Vue Router, etc.).
  2. Kør den lokalt via dev-server. Alt virker.
  3. Deploy på Netlify eller Vercel.
  4. Åbn /dashboard direkte. Få en 404 fra hosten.

Der er to ting jeg tjekker nu:

1. Rewrites / fallback

  • Vercel: i vercel.json eller projekt-setup sørger jeg for en rewrite af alle ikke-static routes til /index.html.
  • Netlify: en _redirects fil med
    /* /index.html 200
  • GitHub Pages: oftest bedst egnet til simple MPAs, men der findes workarounds med 404.html-tricks.

2. Base path og assets

Hvis du hoster under et subpath, fx https://user.github.io/projekt-navn/, skal du typisk sætte et base-felt (Vite) eller bruge Nexts basePath.

Typisk før-fejl:

export default defineConfig({
  // ingen base
});

Efter på GitHub Pages:

export default defineConfig({
  base: "/projekt-navn/",
});

Hvis dine CSS- og JS-filer ikke bliver fundet i produktion, starter jeg altid med at kigge på den slags.

API-kald i produktion: CORS, HTTPS og “det virkede jo lokalt”

Det her er næsten sit eget kursus, men min deploy-tjekliste er ret kort:

  • Base URL peger ikke på localhost
    Søg efter http://localhost igen. Er API-URL’en sat via env var til en rigtig host?
  • HTTPS over det hele
    Hvis frontend ligger på https, men API på http, vil browseren blokere mixed content.
  • CORS kun fejler i produktion
    Lokalt bruger jeg ofte en dev-proxy. I produktion kalder jeg API’et direkte, og så giver det CORS-fejl.

Mit trick her er at åbne Chrome DevTools Network i produktion (ikke kun lokalt). Jeg klikker på et API-kald, ser på Request URL, Response headers og evt. fejl. Jeg har en hel artikel om den tilgang: da jeg første gang åbnede Chrome DevTools Network og alt gav mening.

Caching og gamle filer: deploy uden at deploye

En af de mere irriterende fejl: jeg deployer, men ser stadig den gamle version.

Det er typisk en blanding af:

  • Browser-cache
  • CDN-cache (Netlify/Vercel)
  • Manglende cache-busting i filnavne

Det jeg altid tjekker, når jeg bliver i tvivl:

  1. Åbn siden i incognito og/eller i en anden browser.
  2. I DevTools: Network-tab, sæt “Disable cache” til.
  3. Kig på filnavne: har dine JS/CSS-filer hashes i navnet?
    app.4f3c1a.js er bedre end app.js.

De fleste moderne bundlere (Vite, Webpack, Parcel) tager sig af filnavne, hvis du laver en production build. Men GitHub Pages med håndskrevet HTML og en gammel app.js har ikke den luksus.

Observability light: lidt logs er bedre end ingen

Jeg er ikke på Sentry og fuldt observability-setup i mine små hobbyprojekter. Men jeg prøver at have en eller anden plan for, hvordan jeg opdager fejl i produktion.

Mine minimums-vaner:

  • Hold øje med JavaScript errors
    Åbn DevTools Console i produktion og klik rundt. Se om der dukker røde errors op, du aldrig så i dev.
  • Gem evt. fejl i lokal storage
    Til små projekter kan jeg finde på at lave en lille error-handler, der logger fejl til localStorage med timestamp, så en bruger kan sende mig dem.
  • Brug hostens logs
    Vercel og Netlify viser build logs, og nogle planer har også function logs. Kig dem igennem første gang du deployer noget nyt.

Jeg har tidligere skrevet om at stole mere på logs end på hukommelse: jeg stoler mere på mine logs end på min hukommelse. Det gælder 100 % også her.

Min hurtige “go/no-go” deploy checkliste

Hvis jeg skal være ærlig, så kører jeg sjældent hele romanen ovenfor igennem systematisk. Men jeg har 12 små spørgsmål i mit hoved, som jeg prøver at svare ja til, før jeg trykker deploy.

1-4: Build og miljø

  1. Har jeg kørt en clean install og npm run build lokalt uden fejl?
  2. Matcher Node-versionen nogenlunde det, CI bruger?
  3. Er alle env vars, jeg bruger i koden, også sat i Vercel/Netlify/GitHub Actions?
  4. Har jeg verificeret i logs, at de rigtige env værdier faktisk bruges ved build?

5-8: Routing og API

  1. Har jeg en 404/rewrites-opsætning, så SPA-routes virker ved direkte load?
  2. Er der sat korrekt base path, hvis jeg hoster i et subdirectory?
  3. Er alle API-URLs konfigureret via env vars (ingen hårdkodet localhost)?
  4. Har jeg testet et API-kald i produktion via DevTools Network og set, at det returnerer det forventede?

9-12: Caching og brugeroplevelse

  1. Har jeg åbnet sitet i incognito efter et deploy for at se den “rene” version?
  2. Bruger min bundler hashed filnavne til JS/CSS, så gamle filer ikke hænger fast?
  3. Har jeg klikket igennem de vigtigste flows i produktion, ikke kun lokalt?
  4. Har jeg mindst én måde at opdage fejl på (console check, simple logging eller lignende)?

Hvis jeg går igennem listen og må svare “nej” til mere end et par stykker, så ved jeg egentlig godt, at jeg ikke burde trykke deploy endnu. Nogle gange gør jeg det alligevel, men så fortjener jeg sådan set også de bugs, jeg får.

Jeg synes personligt, at en deploy først er godkendt, når jeg kan lukke min laptop, komme ud på cyklen langs kysten og være rimelig sikker på, at telefonen ikke bipper, før jeg er hjemme igen. Hvis ikke jeg har gjort mig den ulejlighed, så er det ikke et uheld, når produktionen brænder, det er bare dovenskab forklædt som optimisme.

Servér build-mappen med en simpel static server (fx npx serve -s dist eller vite preview) så du får samme fil- og base-path-adfærd som i produktion. Sørg for history-fallback for single-page apps, så ukendte ruter returnerer index.html, og test både rod- og deep-links manuelt i browseren.
Nej - alt, der skal forblive hemmeligt, må ikke pakkes ind i frontend, fordi buildet eksponerer værdierne til slutbrugeren. Brug i stedet en backend eller serverless-funktion til at håndtere sensitive nøgler og only inject public konfiguration i frontend via VITE_-prefiks eller tilsvarende.
Opret en CI-job der kører clean install, build, lint og evt. en kort smoke-test (fx serve + HEAD-request til / og en deep-link), og lad pipeline fejle ved mismatch eller manglende index.html. Du kan bruge GitHub Actions eller tilsvarende til at fange miljø-, versions- og build-fejl før deploy.
Tjek forskelle i node- og package-manager-versioner, lockfile-synkronisering, case-sensitiv filsystemadfærd (macOS vs Linux), og native dependencies der kræver ekstra build-tools i CI. Sørg også for at miljøvariabler der bruges ved buildtid er sat i CI, da manglende vars ofte er årsagen.

Mikkel Schrøder er den dér stille type, der i årevis har siddet om aftenen med en kop kaffe og et åbent kodeprojekt, mens resten af huset er ved at falde til ro. Hans interesse for kodning startede, da han som teenager forsøgte at lave en simpel hjemmeside til sit favorit-fodboldhold og opdagede, at man kunne ændre alt ved at rode med HTML og CSS. Siden har han lært tingene ved at prøve sig frem, læse forumtråde og pille ved små projekter, indtil de gjorde det, han ville.

På Coding Class deler han ikke perfekte løsninger fra et glansbillede-univers, men de ting han faktisk selv har bokset med: mærkelige JavaScript-fejl, CSS der ikke opfører sig som forventet, og små Python-scripts, der starter i kaos og ender med at spare tid i hverdagen. Han kan godt lide at vise både den første, halvdårlige løsning og den forbedrede udgave, så du kan se forskellen og forstå tankegangen bag.

Mikkel brænder for at gøre programmering mindre skræmmende for dem, der ikke ser sig selv som "tech-typer". Derfor skriver han på helt almindeligt dansk, med små, konkrete kodeeksempler og fokus på, hvordan du selv kan komme fra teori til noget, der faktisk virker. På Coding Class forsøger han at bygge bro mellem manual-sproget og virkeligheden ved at vise, hvordan det føles at sidde med fejlen klokken 22.30 – og hvad der skulle til, før den forsvandt.

Send kommentar

You May Have Missed