{"version":"https://jsonfeed.org/version/1.1","title":"Engineer — Platform — Portefølje og noter","home_page_url":"https://platform.engineer.company/da/","feed_url":"https://platform.engineer.company/da/feed.json","description":"Engineer ApS — softwareudvikling \u0026 IT-rådgivning i København, Danmark. En portefølje af dokumenterede ingeniørresultater inden for software, data, cloud og IT.","language":"da","icon":"https://platform.engineer.company/assets/images/brand/card.webp","favicon":"https://platform.engineer.company/assets/icons/apple/apple-touch-icon.png","authors":[{"name":"Engineer ApS","url":"https://platform.engineer.company/da/"}],"items":[{"id":"https://platform.engineer.company/da/portfolio/developed-and-launched-the-company-s-first-observability-9/","url":"https://platform.engineer.company/da/portfolio/developed-and-launched-the-company-s-first-observability-9/","title":"Har udviklet og lanceret virksomhedens første observability‑dashboard, der leverer indsigt i systemets ydeevne i realtid og datavisualisering på det store kontor‑TV.","summary":"Byggede virksomhedens første observability-dashboard til realtidsindsigt — teams opdagede og løste incidents 40 % hurtigere.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Virksomheden havde udfordringer med at overvåge systemets ydeevne i realtid, hvilket ofte førte til forsinket incidenthåndtering og reduceret indblik i infrastrukturens tilstand. Der fandtes ingen central løsning, hvor teams kunne få indsigt i driftsmetrikker.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Opgaven var at udvikle en løsning, der ville gøre det muligt for både tekniske og ikke‑tekniske interessenter at overvåge centrale systemmetrikker i realtid med fokus på tilgængelighed, klarhed og proaktiv fejlopsporing.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Virksomhedens første observability‑dashboard blev designet og implementeret og indsamlede alle væsentlige systemmetrikker — CPU, RAM, HDD, temperatur med mere — fra fjerne Linux‑servere via SSH, og selv Docker‑containere blev overvåget på denne måde. Senere blev en anden version designet og implementeret med Grafana og Prometheus til mere avanceret visualisering. Samarbejde med DevOps- og engineering‑teams identificerede kritiske metrikker, datapipelines blev konfigureret til at indsamle og behandle ydeevnemetrikker, og dashboardet udrullet på en stor kontor‑TV‑skærm for maksimal synlighed. Alarmering ved overskridelse af tærskler blev også integreret.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Dashboardet forbedrede markant systemets gennemsigtighed og responstiden på driftsproblemer. Teams kunne opdage og løse incidents 40 % hurtigere. Det fremmede også en kultur af fælles ejerskab over systemets tilstand ved at gøre ydeevnedata tilgængelige for alle på kontoret, hvilket i sidste ende bidrog til et mere stabilt og effektivt produktionsmiljø.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Containere (Docker/Kubernetes)","Dataanalyse","DevOps","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Monitorering \u0026 observability","Dataanalyse \u0026 BI‑dashboards","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/designed-deployed-and-maintained-10-postgresql-and-ms-13/","url":"https://platform.engineer.company/da/portfolio/designed-deployed-and-maintained-10-postgresql-and-ms-13/","title":"Har designet, idriftsat og vedligeholdt 10 PostgreSQL- og MS SQL‑servere på Ubuntu Linux VPS og sikret optimal serverydeevne og pålidelighed.","summary":"Designede og vedligeholdt 10 PostgreSQL- og MS SQL-servere på Ubuntu Linux VPS med 99,9 % oppetid og 30 % hurtigere forespørgsler.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Som DevOps‑ingeniør i en mellemstor teknologivirksomhed var opgaven at administrere og optimere databaseinfrastrukturen til støtte for en voksende brugerbase og forretningskritiske applikationer. Organisationen var stærkt afhængig af PostgreSQL og Microsoft SQL Server og krævede høj tilgængelighed, skalerbarhed og sikkerhed.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Det primære ansvar var at arkitektere, udrulle, konfigurere og vedligeholde 10 PostgreSQL- og MS SQL Server‑instanser i Ubuntu Linux VPS‑miljøer med optimal ydeevne, robuste sikkerhedsprotokoller og proaktiv overvågning, samt at skalere infrastrukturen til fremtidig vækst uden at gå på kompromis med omkostninger og compliance.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e PostgreSQL 14 og MS SQL Server 2019 blev installeret og konfigureret på Ubuntu 20.04 LTS, automatiserede backups sat op med pg_dump og SQL Server Agent‑jobs med retention og offsite‑lagring, og serverkonfigurationer optimeret (hukommelsesallokering, query caching, connection pooling). Overvågning blev implementeret med Prometheus og Grafana, regelmæssig patching af databaser og OS udført, firewalls (UFW) og RBAC konfigureret og disaster recovery‑procedurer dokumenteret, herunder point‑in‑time‑gendannelse og failover.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e 99,9 % oppetid blev opnået på tværs af alle 10 databaseservere, svartider forbedret med 30 % gennem konfigurationstuning og indeksoptimering, manuelt vedligeholdelsesarbejde reduceret med 50 % via automatisering (over 10 timer om måneden frigjort) og infrastrukturen skaleret til at understøtte en 40 % stigning i brugertrafik uden servicetab — hvilket bidrog til 20 % omsætningsvækst i det følgende kvartal. CTO\u0026rsquo;en anerkendte implementeringen af sikkerheds‑best‑practices, der forhindrede potentielle databrud.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Databaser","DevOps","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Monitorering \u0026 observability","Performanceoptimering","PostgreSQL","Sikkerhed","Systemadministration","Databaseadministration (DBA)","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/enhanced-data-security-by-implementing-1-000-rbac-14/","url":"https://platform.engineer.company/da/portfolio/enhanced-data-security-by-implementing-1-000-rbac-14/","title":"Har forbedret datasikkerheden ved at implementere 1.000 RBAC‑regler for udviklere, applikationsinstanser, PostgreSQL, MS SQL og andre Linux‑servere for at forhindre uautoriseret adgang; dokumenteret med Ansible‑automatisering.","summary":"Styrkede datasikkerheden med 1.000 RBAC-regler på tværs af udviklere, apps og Linux/DB-servere — 85 % lavere adgangsrisiko, Ansible-drevet.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Organisationen havde behov for at styrke adgangskontrollen på tværs af flere systemer, herunder udviklermiljøer, applikationsinstanser, PostgreSQL, MS SQL og Linux‑servere. Selvom den underliggende RBAC‑model var enkel, lå udfordringen i at håndtere over 1.000 individuelle regler for forskellige brugerroller og systemkrav uden at introducere kompleksitet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At implementere en skalerbar RBAC‑løsning ved at definere og håndhæve over 1.000 regler for adgangskontrol. Det indebar at mappe rettigheder til specifikke roller (udviklere, applikationsinstanser, databaseadministratorer) og at anvende reglerne konsistent på tværs af alle systemer, samt at dokumentere reglerne og automatisere deres udrulning.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Tilgangen fokuserede på at skabe en enkel, modulær RBAC‑struktur, med rettigheder brudt ned i klare, genanvendelige kategorier (f.eks. \u0026ldquo;read‑only‑adgang til produktionsdatabaser\u0026rdquo;). Med Ansible blev konfigurationen af hver regel automatiseret og konsistens sikret på tværs af miljøer: udviklere fik kun adgang til deres tildelte servere, mens applikationsinstanser havde begrænsede rettigheder for at forhindre lateral bevægelse. Processen prioriterede klarhed frem for kompleksitet, med hver regel eksplicit knyttet til en rolle og et system.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Implementeringen sikrede over 1.000 regler uden unødvendig kompleksitet og reducerede risikoen for uautoriseret adgang med 85 %. Automatiseringen strømlinede udrulningen og reducerede opsætningstiden med 60 % sammenlignet med manuelle metoder. Den dokumenterede ramme gjorde det muligt for teams hurtigt at revidere eller ændre regler og sikrede skalerbarhed, efterhånden som infrastrukturen voksede.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Databaser","DevOps","Dokumentation","Infrastruktur","Linux \u0026 servere","Sikkerhed","Systemadministration","Databaseadministration (DBA)","Infrastructure as Code","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/automated-gis-saas-application-deployment-data-processing-and-16/","url":"https://platform.engineer.company/da/portfolio/automated-gis-saas-application-deployment-data-processing-and-16/","title":"Har automatiseret udrulning af GIS SaaS‑applikationer, databehandling og rapporteringssystem ved hjælp af GitHub Actions CI/CD, Python, Bash og SQL.","summary":"Automatiserede GIS SaaS-udrulning, databehandling og rapportering med GitHub Actions CI/CD, Python, Bash og SQL — pålidelige releases.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Hos Utiligize var det at få GIS SaaS‑appen udrullet, behandle dens data og producere rapporterne alt sammen manuelle trin — og manuelle trin er både langsomme og stille farlige. Hver release åd engineering‑tid og bar chancen for en fejl, og det tilbagevendende data- og rapporteringsarbejde sad der og åd kapacitet uge efter uge.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At automatisere hele vejen fra kode til produktion, plus det tilbagevendende databehandlings- og rapporteringsarbejde, var opgaven — målet var releases, der var hurtige, sikre og reproducerbare frem for et omhyggeligt manuelt ritual hver gang.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Hele vejen blev automatiseret. GitHub Actions‑pipelines overtog test‑build‑deploy‑cyklussen, så en release holdt op med at afhænge af, at nogen huskede trinene. Den tilbagevendende databehandling og rapporterne flyttede over i planlagte Python-, Bash- og SQL‑jobs, så de bare kørte i stedet for at være nogens pligt. Og konfigurationen og secrets blev standardiseret, så hvert miljø opførte sig ens — hvilket er det, der dræber \u0026ldquo;det virker på min maskine\u0026rdquo;-overraskelserne, for der holder op med at være en \u0026ldquo;min maskine\u0026rdquo;, der er anderledes end produktion.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Udrulning, databehandling og rapportering blev alle automatiserede og pålidelige, det manuelle slid kom af teamets bord, og releasecyklussen blev kortere. Teamet kunne rette sin opmærksomhed mod produktet i stedet for driften, der var pakket omkring det.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Cloud","Data engineering","Datapipelines (ETL/ELT)","DevOps","Drift \u0026 backup","GIS / Geospatial","Python","SQL","DevOps \u0026 CI/CD‑automatisering","GIS \u0026 geospatiale løsninger","Udvikling af datapipelines (ETL/ELT)"]},{"id":"https://platform.engineer.company/da/portfolio/automated-delivery-of-20-gis-data-pipelines-and-17/","url":"https://platform.engineer.company/da/portfolio/automated-delivery-of-20-gis-data-pipelines-and-17/","title":"Har automatiseret levering af 20 GIS‑datapipelines og ETL‑processer for appdata og strømlinet infrastrukturautomatisering og rapportering.","summary":"Automatiserede 20 GIS-datapipelines og ETL-processer for appdata — strømlinet infrastrukturautomatisering og pålidelige, aktuelle data.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Platformen kørte på en masse GIS‑datapipelines og ETL‑processer for appdata, og de blev leveret og overvåget i hånden. Håndkørte pipelines skaber flaskehalse, de driver ud af konsistens, og værst af alt bærer de en konstant lav risiko for, at en stille fejler, og ingen bemærker det, før dataene allerede er forkerte længere nede.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At automatisere leveringen af de pipelines og ETL‑processer — så dataene flød pålideligt og forudsigeligt uden nogen til at føre dem igennem — var opgaven.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Tyve GIS‑datapipelines og ETL‑processerne for appdata kom under automatiseret levering, fra ende til anden. Planlægningen, loggingen og fejlhåndteringen blev standardiseret, så hver pipeline opførte sig ens og, afgørende, man kunne se, når en ikke gjorde — en stille fejl er kun stille, hvis intet holder øje. Og de blev foldet ind i den eksisterende infrastrukturautomatisering og rapportering, så de var en del af ét sammenhængende system frem for en skuffe fuld af scripts, nogen skulle huske at køre.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Alle tyve pipelines og deres ETL kørte automatisk og forudsigeligt, og hele billedet af infrastrukturautomatisering og rapportering blev pænere for det. Forretningen fik pålidelige, aktuelle data, uden at nogen skulle gå dem igennem i hånden — og uden den stille‑fejl‑risiko hængende over sig.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Data engineering","Datapipelines (ETL/ELT)","DevOps","Drift \u0026 backup","GIS / Geospatial","Monitorering \u0026 observability","DevOps \u0026 CI/CD‑automatisering","GIS \u0026 geospatiale løsninger","Udvikling af datapipelines (ETL/ELT)"]},{"id":"https://platform.engineer.company/da/portfolio/automated-100-critical-data-backups-using-barman-google-18/","url":"https://platform.engineer.company/da/portfolio/automated-100-critical-data-backups-using-barman-google-18/","title":"Har automatiseret 100 kritiske databackups ved hjælp af Barman, Google Cloud, Bash og Python og sikret dataintegritet på tværs af databaser.","summary":"Automatiserede 100 kritiske databackups med Barman, Google Cloud, Bash og Python — datagenoprettelse gik fra antagelse til testet faktum.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e De kritiske GIS‑data, applikationsinstanserne og databaserne var spredt over systemer med backups, der var inkonsistente og delvist manuelle. For et produkt, der lever på sine data, er det ikke en risiko, man kan lade ligge — og den dag, man faktisk har brug for en backup, er præcis den værste dag at finde ud af, at den var ufuldstændig.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Opgaven var at garantere, at alle de kritiske data kunne gendannes, hvilket betød at automatisere omfattende, verificerede backups på tværs af hele estatet — verificerede som det ord, der betyder noget.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Backup‑regimet blev bygget fra ende til anden. Hundrede kritiske databackups blev automatiseret, med Barman på PostgreSQL‑siden og Google Cloud, der holdt offsite‑kopierne, og det hele blev orkestreret og valideret med Bash og Python — for en backup, man har taget, men aldrig tjekket, er ikke rigtig en backup, det er et håb. Så der var retention‑politikker til at holde dem aktuelle og integritetstjek til at bekræfte, at hver enkelt faktisk var god, ikke bare til stede.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Backups kørte automatisk og kunne verificeres på tværs af hver database, hvilket gjorde datagenoprettelighed fra en antagelse til noget testet. En væsentlig driftsrisiko kom af forretningen og blev erstattet af en genopretningsvej, man faktisk kunne stole på — forskellen var, at denne var tjekket, ikke bare konfigureret.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Cloud","Databaser","DevOps","Drift \u0026 backup","Infrastruktur","PostgreSQL","Python","Backup \u0026 disaster recovery","Databaseadministration (DBA)","DevOps \u0026 CI/CD‑automatisering"]},{"id":"https://platform.engineer.company/da/portfolio/deployed-and-maintained-20-docker-containerized-applications-troubleshooting-19/","url":"https://platform.engineer.company/da/portfolio/deployed-and-maintained-20-docker-containerized-applications-troubleshooting-19/","title":"Har udrullet og vedligeholdt 20 Docker‑containeriserede applikationer, fejlfundet med Podman og Kubernetes og administreret R‑baserede apps på Google Cloud og AWS.","summary":"Udrullede og vedligeholdt 20 Docker-containeriserede apps på Google Cloud og AWS — fejlfinding med Podman og Kubernetes, stabile releases.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Der var et voksende sæt containeriserede applikationer — herunder nogle R‑baserede analyseapps — der kørte på tværs af både Google Cloud og AWS. Spredt over to clouds og et par container‑runtimes skulle de udrulles konsistent og kunne diagnosticeres hurtigt, når noget gik galt, hvilket er sværere, end det lyder, når ikke to miljøer er helt ens.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At udrulle og vedligeholde de workloads pålideligt, og at kunne diagnosticere problemer hurtigt på tværs af runtimes og clouds, var jobbet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Det containeriserede estate blev administreret på tværs af begge clouds — tyve Docker‑applikationer udrullet og vedligeholdt med konsistent konfiguration og overvågning, så de ikke hver var deres eget snefnug. Når ting gik galt, gik fejlfindingen gennem Podman og orkestreringsdebugging gennem Kubernetes. De R‑baserede analyseapps fik dedikeret opmærksomhed i Google Cloud- og AWS‑produktionsmiljøerne, holdt stabile og reproducerbare, hvilket for analyse betyder noget — et resultat, man ikke kan reproducere, er ikke meget af et resultat.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Det containeriserede estate kørte pålideligt på tværs af begge clouds, problemer blev diagnosticeret hurtigere, og udrulninger forblev stabile og reproducerbare. De applikationer, produktet lænede sig op ad, forblev pålidelige uanset hvilken cloud de nu tilfældigvis kørte i.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Cloud","Containere (Docker/Kubernetes)","Dataanalyse","DevOps","Drift \u0026 backup","Infrastruktur","Cloud‑infrastruktur \u0026 migrering","Containerisering \u0026 orkestrering","DevOps \u0026 CI/CD‑automatisering"]},{"id":"https://platform.engineer.company/da/portfolio/managed-30-ubuntu-linux-vps-instances-implementing-disaster-20/","url":"https://platform.engineer.company/da/portfolio/managed-30-ubuntu-linux-vps-instances-implementing-disaster-20/","title":"Har administreret 30 Ubuntu Linux VPS‑instanser, implementeret disaster recovery‑strategier og sikret optimale netværkskonfigurationer.","summary":"Administrerede 30 Ubuntu Linux VPS-instanser med testede disaster recovery-strategier og solid netværksdrift — et robust vækstfundament.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Utiligize kørte på en flåde af Ubuntu Linux VPS‑instanser, hvis opsætning var vokset organisk over tid — hvilket er en pæn måde at sige, den var akkumuleret frem for designet. Det efterlod huller: inkonsistente konfigurationer og genopretnings- og netværksarrangementer, der var mere historisk tilfældighed end plan.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Opgaven var at få flåden under ordentlig styring, styrke disaster recovery‑siden og gøre netværkskonfigurationen konsistent og fornuftig på tværs af hver instans.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e De tredive instanser kom under bevidst styring — behandlet som én sammenhængende flåde frem for tredive individuelle kæledyr. Rigtig disaster recovery gik ind: backups, og gendannelsesprocedurer, der faktisk blev testet, for en utestet gendannelse er bare en teori. Og netværkskonfigurationen blev standardiseret for sikkerhed og ydeevne, så hver instans fulgte den samme hærdede baseline i stedet for, hvad den nu tilfældigvis var endt med.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Infrastrukturen blev robust og konsistent, med genopretningsveje, der var testet, og netværksdrift, man kunne stole på. Nedetidsrisikoen faldt, og forretningen endte med et solidt fundament at vokse på frem for en lappeløsning, den skulle blive ved med at passe.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["DevOps","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Netværk \u0026 VPN","Sikkerhed","Systemadministration","Backup \u0026 disaster recovery","Netværk \u0026 VPN‑opsætning"]},{"id":"https://platform.engineer.company/da/portfolio/prevented-security-breaches-by-leading-access-management-initiatives-21/","url":"https://platform.engineer.company/da/portfolio/prevented-security-breaches-by-leading-access-management-initiatives-21/","title":"Har forhindret sikkerhedsbrud ved at lede initiativer inden for adgangsstyring med M365, 1Password, Red Hat SSO og OKTA SSO.","summary":"Ledede adgangsstyring med M365, 1Password, Red Hat SSO og OKTA — markant lavere risiko for uautoriseret adgang og sporbar adgang.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Adgang til systemer og tjenester på tværs af Utiligize blev håndteret inkonsistent — rettigheder givet ad hoc, over tid, af forskellige folk. Det er et dobbelt problem: det åbner døren for adgang, ingen havde tænkt sig, og det gør revision næsten umulig, for ingen kan faktisk sige, hvem der kan nå hvad, eller hvorfor de kan.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Målet var at lukke sikkerhedseksponeringen ved at centralisere og stramme adgangsstyringen på tværs af hele organisationen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Eftersynet konsoliderede identiteterne og adgangen på tværs af M365, 1Password, Red Hat SSO og OKTA SSO, så der var et sammenhængende billede i stedet for spredte rettigheder pr. system. Least privilege blev håndhævet — personer og systemer havde præcis, hvad de havde brug for, og intet i overskud — og on- og offboarding standardiseret, så adgang blev givet og, lige så vigtigt, tilbagekaldt hurtigt og konsistent frem for at hænge ved, efter nogen var gået videre.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Risikoen for uautoriseret adgang faldt markant, og adgang blev sporbar og konsistent — man kunne endelig svare på \u0026ldquo;hvem kan nå det her, og hvorfor.\u0026rdquo; Det mærkelige er, at det også gjorde hverdagen enklere for teamet: de rigtige døre åbnede let, og de forkerte forblev lukkede, hvilket er, hvad god adgangsstyring faktisk føles som.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Cloud","Infrastruktur","Sikkerhed","Systemadministration","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/mitigated-operational-risks-by-implementing-a-monitoring-dashboard-22/","url":"https://platform.engineer.company/da/portfolio/mitigated-operational-risks-by-implementing-a-monitoring-dashboard-22/","title":"Har reduceret driftsrisici ved at implementere et monitoreringsdashboard med Grafana og Prometheus og forbedret systemets pålidelighed.","summary":"Byggede et Grafana + Prometheus-monitoreringsdashboard, der fanger problemer, før de eskalerer — fra brandslukning til forebyggelse.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Problemer hos Utiligize blev som regel først bemærket, efter de allerede havde ramt brugerne, fordi der ikke fandtes ét samlet overblik over, hvordan systemerne havde det. Uden det indblik var teamet permanent på bagfod — reagerede på ting, der allerede var gået galt, i stedet for at se dem komme.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Målet var at skære driftsrisikoen ned ved at give teamet realtidsindblik i de systemer, de var afhængige af.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Observability‑laget blev bygget ud. Et monitoreringsdashboard på Grafana og Prometheus, de centrale tjenester instrumenteret, og — den del, der faktisk betyder noget — metrikker, der betød noget, frem for forfængelighedstal, der ser travle ud og fortæller en ingenting. Så alarmtærskler sat på dem, synliggjort der, hvor teamet faktisk ville se dem og kunne handle, mens der stadig var tid at handle i.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Problemer begyndte at blive fanget og håndteret, før de eskalerede, og systemets pålidelighed blev bedre for det. Teamet skiftede fra reaktiv brandslukning til noget roligere og mere proaktivt — fangede problemer, mens de stadig var små nok til at være kedelige.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Drift \u0026 backup","Infrastruktur","Monitorering \u0026 observability","DevOps \u0026 CI/CD‑automatisering","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/managed-and-troubleshooted-8-wireguard-vpn-and-ipsec-23/","url":"https://platform.engineer.company/da/portfolio/managed-and-troubleshooted-8-wireguard-vpn-and-ipsec-23/","title":"Har administreret og fejlfundet 8 WireGuard VPN- og IPSEC VPN‑forbindelser og sikret sikker kommunikation på tværs af Google Cloud og Linux‑systemer.","summary":"Administrerede og fejlfandt 8 WireGuard- og IPSEC VPN-forbindelser på Google Cloud og Linux — stoppede tilbagevendende forbindelsesudfald.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Sikker forbindelse mellem cloud\u0026rsquo;en og on‑premises‑Linux‑systemerne kørte over flere VPN‑tunneler, og de var skrøbelige og bøvlede at diagnosticere, når de faldt. En død tunnel kunne kappe kommunikationen mellem miljøer, og at fejlfinde en var langsomt og usikkert — man var aldrig helt sikker på, om man havde fundet den rigtige årsag.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At administrere og fejlfinde de forbindelser, for at garantere sikker og uafbrudt kommunikation, var jobbet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e VPN‑estatet kom under kontrol — otte WireGuard- og IPSec‑tunneler på tværs af Google Cloud og Linux‑systemerne, administreret og fejlfundet som et sæt frem for otte separate mysterier. Deres konfiguration blev standardiseret, så de var konsistente og forståelige i stedet for hver at være sit eget særtilfælde, og deres tilstand blev overvåget, med de tilbagevendende routing- og key‑exchange‑problemer tacklet ved roden frem for lappet over med en genstart.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Alle otte tunneler kørte sikkert og pålideligt, kommunikationen mellem miljøer forblev beskyttet, og de tilbagevendende forbindelsesproblemer, der plejede at afbryde arbejdet, holdt op med at ske. At rette rodårsagerne frem for at pleje symptomerne er det, der gjorde dem fra en tilbagevendende hovedpine til noget, der bare virkede.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Cloud","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Netværk \u0026 VPN","Sikkerhed","Systemadministration","Netværk \u0026 VPN‑opsætning","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/improved-team-communication-and-collaboration-by-implementing-slack-34/","url":"https://platform.engineer.company/da/portfolio/improved-team-communication-and-collaboration-by-implementing-slack-34/","title":"Har forbedret teamets kommunikation og samarbejde ved at implementere Slack, Mattermost, 1Password og Jira og sparet 8.000 arbejdstimer.","summary":"Sparede ~8.000 timer ved at indføre Slack, Mattermost, 1Password og Jira — mindre jagt på information, mere faktisk levering.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Efterhånden som teamet blev større, havde kommunikationen og værktøjerne ikke fulgt med, og det kunne mærkes. Ting blev sagt ét sted og overset af dem, der havde brug for dem, arbejde blev lavet dobbelt, fordi ingen kunne se, hvad en anden allerede havde gjort, og at koordinere noget som helst tog længere tid end selve arbejdet. Den slags friktion er usynlig fra dag til dag, men den løber op i en masse tabt tid.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Målet var at rette op på, hvordan teamet kommunikerede og arbejdede sammen, og kradse den tid tilbage, der stille og roligt blev blødt til al den friktion.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Værktøjerne blev indført og standardiseret, og — det er den del, der faktisk betyder noget — praksis for at bruge dem blev sat, så de ikke bare blev endnu et sted at tjekke. Slack og Mattermost til kommunikation, 1Password, så delte secrets ikke blev sendt rundt på måder, ingen kunne holde styr på, Jira, så arbejdet blev tracket ét sted i stedet for at leve i folks hoveder og indbakker. Værktøjerne var den nemme del; at få alle til faktisk at bruge dem på samme måde var det egentlige arbejde.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Kommunikation og samarbejde blev mærkbart bedre, og det strømlinede opsæt sparede noget i størrelsesordenen 8.000 arbejdstimer — tid, der havde gået til at jage information og lave arbejde om, som nu gik til faktisk levering.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Agile \u0026 Scrum","Automatisering \u0026 CI/CD","Dokumentation","Projektledelse","Sikkerhed","Teamledelse","Projektledelse (Agile)","Sikkerhed \u0026 adgangsstyring","Teamopbygning \u0026 mentoring"]},{"id":"https://platform.engineer.company/da/portfolio/overhauled-internal-processes-saving-8-000-hours-by-38/","url":"https://platform.engineer.company/da/portfolio/overhauled-internal-processes-saving-8-000-hours-by-38/","title":"Har gennemgribende fornyet interne processer og sparet 8.000 timer ved at forbedre softwarearkitektur, systemer og planlægningseffektivitet.","summary":"Fornyede interne processer og sparede ~8.000 timer ved at forbedre softwarearkitektur, systemer og planlægningseffektivitet.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Måden, tingene blev gjort internt på, havde samlet den sædvanlige rust — softwarearkitektur, der var vokset ved aflejring frem for design, systemer, der virkede, men ikke effektivt, planlægning, der lod folk enten vente eller være pressede. Intet af det brændte, hvilket er præcis derfor, det var blevet ladt i fred, men det kostede stille og roligt en masse tid.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Målet var at forny de processer gennemgribende — at gå ud og finde spildet og tage det ud frem for at blive ved med at betale for det.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e De interne processer blev omarbejdet på tre fronter: softwarearkitekturen, så den var noget, man kunne ræsonnere om og bygge på i stedet for at arbejde uden om; systemerne, strømlinet, så det rutineprægede arbejde holdt op med at tage længere tid, end det burde; og planlægningen, så kapaciteten faktisk blev matchet med arbejdet. Og ændringerne blev gjort til at sidde fast — forankret i, hvordan teamet opererede, frem for efterladt som et notat, alle nikkede til og glemte — for procesforbedringer, der ikke gøres permanente, forfalder bare tilbage til den gamle måde.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Fornyelsen sparede omkring 8.000 timer ved at gøre arkitekturen, systemerne og planlægningen mærkbart mere effektive. Det er kapacitet, der gik direkte tilbage i arbejde med højere værdi i stedet for i overhead, ingen havde tænkt på at stille spørgsmål ved.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Infrastruktur","Løsningsarkitektur","Performanceoptimering","Platformarkitektur","Projektledelse","Teknisk ledelse","DevOps \u0026 CI/CD‑automatisering","Platform- \u0026 løsningsarkitektur","Projektledelse (Agile)","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/administered-network-infrastructure-for-over-1-000-servers-39/","url":"https://platform.engineer.company/da/portfolio/administered-network-infrastructure-for-over-1-000-servers-39/","title":"Har administreret netværksinfrastruktur for over 1.000 servere og sikret optimal systemudrulning, sikkerhed og fejlfinding.","summary":"Administrerede netværksinfrastruktur for 1.000+ servere — pålidelig udrulning, solid sikkerhed og prompt fejlfinding.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Virksomhedens drift sad oven på et stort serverestate — over tusind af dem — og et estate af den størrelse holder sig ikke pålideligt af sig selv. Udrulning, sikkerhed og den stadige strøm af ting, der går galt, kræver alle en, der kører dem med faktisk disciplin, ellers bliver det hele ustabilt, og ingen er helt sikre på hvorfor.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At administrere den netværksinfrastruktur var jobbet — at holde den sikker, holde den pålidelig, holde den konsistent i en skala, hvor inkonsistens er det, der slår en ihjel.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Netværksinfrastrukturen kørte på tværs af mere end tusind servere. Hvordan systemer blev udrullet blev standardiseret, så en server kom op på den samme forudsigelige måde i stedet for hver at være en lille smule specialbygget; sikkerheden blev hærdet frem for at stole på, at ingen ville komme og lede; og fejlfindingen blev håndteret, når noget først gik i stykker. I den skala er standardiseringen det, der redder en — tusind snefnug er uadministrerbart, tusind af den samme ting er bare arbejde.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Estatet kørte med pålidelig udrulning, solid sikkerhed og problemer, der blev håndteret prompte i stedet for at ulme. Det er den slags infrastrukturarbejde, der er usynligt, når det går godt, hvilket er pointen — det var den stabile rygrad, alt andet i virksomheden kørte på.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Netværk \u0026 VPN","Sikkerhed","Systemadministration","Netværk \u0026 VPN‑opsætning","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/automated-ssl-tls-certificate-creation-for-100-docker-41/","url":"https://platform.engineer.company/da/portfolio/automated-ssl-tls-certificate-creation-for-100-docker-41/","title":"Har automatiseret oprettelse af SSL/TLS‑certifikater for 100 Docker‑applikationer og sikret sikre forbindelser på tværs af Ubuntu Linux‑hosts.","summary":"Automatiserede SSL/TLS-certifikater for 100 Docker-apps på Ubuntu Linux — slut med manuelle fornyelser og udløbsnedbrud.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Hundrede Docker‑applikationer havde alle brug for SSL/TLS‑certifikater, og certifikater er den slags, der er fint lige indtil de ikke er. At udstede og forny hundrede af dem i hånden er langsomt, det er kedeligt, og det er præcis den slags manuelle job, hvor én glemt fornyelse tager en app ned med en udløbsfejl på det værst tænkelige tidspunkt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Målet var oprettelse og fornyelse af certifikater automatiseret — hver app med gyldig, betroet kryptering, og ingen der skulle huske at gøre noget.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Et ACME‑baseret workflow håndterede hele livscyklussen for de hundrede Docker‑applikationer — oprettede certifikaterne og fornyede dem, før de udløb — og udrullede dem automatisk på tværs af Ubuntu Linux‑hostene, der kørte en blanding af Apache og Nginx. Hele målet var at tage mennesket ud af det, for mennesket er den del, der glemmer.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Alle hundrede applikationer holdt gyldige certifikater og sikre forbindelser af sig selv. Det manuelle certifikatarbejde forsvandt bare, og med det hele den kategori af nedbrud, hvor noget går i stykker, ikke fordi det fejlede, men fordi et certifikat stille og roligt udløb, og ingen bemærkede det.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Containere (Docker/Kubernetes)","DevOps","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Sikkerhed","Containerisering \u0026 orkestrering","DevOps \u0026 CI/CD‑automatisering","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/streamlined-ci-cd-processes-saving-4-000-hours-42/","url":"https://platform.engineer.company/da/portfolio/streamlined-ci-cd-processes-saving-4-000-hours-42/","title":"Har strømlinet CI/CD‑processer og sparet 4.000 timer ved at indføre automatisering i softwareudviklingspipelines.","summary":"Strømlinede CI/CD og sparede ~4.000 timer — hurtigere, mere pålidelige releases, så teamet kunne udgive med tillid.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e At få software ud ad døren afhang af manuelle, inkonsistente trin — nogen der huskede rækkefølgen og gjorde det en anelse forskelligt hver gang — og det bremsede releases og åd engineering‑timer, der skulle være gået til at bygge ting.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Målet var at strømline CI/CD‑processen og få automatisering ind i pipelines, så releases holdt op med at være et manuelt ritual.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Automatiserede build-, test- og udrulningspipelines blev sat op, så vejen fra en ændring til, at den kørte i produktion, var standardiseret i stedet for improviseret. De gentagne manuelle trin — dem, der var langsomme og, værre, blev gjort forskelligt afhængigt af, hvem der gjorde dem — kom ud. Når først pipelinen gør det på samme måde hver gang, holder en hel klasse af \u0026ldquo;det virkede på min maskine\u0026rdquo; og halvt huskede udrulningstrin bare op med at ske.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Automatiseringen gav omkring 4.000 timer tilbage og gjorde releases både hurtigere og mere pålidelige. Teamet kunne udgive uden at spænde ben for det — tilliden kom fra, at processen var konsistent, ikke fra at alle var forsigtige.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Drift \u0026 backup","Teknisk ledelse","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/streamlined-data-analysis-and-software-development-processes-saving-43/","url":"https://platform.engineer.company/da/portfolio/streamlined-data-analysis-and-software-development-processes-saving-43/","title":"Har strømlinet processer for dataanalyse og softwareudvikling og sparet 4.000 timer ved at indføre CI/CD‑praksis med GitHub, GitLab, Bash og Python.","summary":"Sparede ~4.000 timer ved at indføre CI/CD med GitHub, GitLab, Bash og Python — hurtigere dataanalyse og udvikling, mere konsistent.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Både dataanalysearbejdet og softwareudviklingen blev holdt tilbage af det samme: manuelle processer. Arbejde bevægede sig fra udvikling til levering på en langsom, inkonsistent måde, og analysesiden havde sin egen bunke gentagne trin, som nogen lavede i hånden hver gang.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Målet var at strømline begge dele ved at bringe moderne automatisering og CI/CD‑praksis til workflows, der ikke havde haft dem.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e CI/CD‑praksis blev indført, bygget på GitHub og GitLab, med Bash og Python, der lavede automatiseringsarbejdet nedenunder. De gentagne trin på tværs af både dataanalyse- og udviklingsworkflows blev automatiseret, og hvordan arbejdet bevægede sig fra udvikling og hele vejen til levering blev standardiseret, så det var det samme hver gang frem for genopfundet pr. projekt. At bringe analysesiden ind i den samme disciplinerede pipeline som udviklingsarbejdet var en stor del af det — den var blevet behandlet som en separat, mere manuel verden.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e De strømlinede processer sparede omkring 4.000 timer og satte fart på både dataanalysen og softwareudviklingen, og lige så nyttigt gjorde de det, der blev sendt ud, mere konsistent — færre overraskelser fra arbejde, der var blevet gjort en anelse forskelligt hver gang.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Data engineering","Datapipelines (ETL/ELT)","Dataanalyse","DevOps","Python","Dataanalyse \u0026 BI‑dashboards","DevOps \u0026 CI/CD‑automatisering","Udvikling af datapipelines (ETL/ELT)"]},{"id":"https://platform.engineer.company/da/portfolio/automated-data-processing-tasks-using-shell-scripting-pl-45/","url":"https://platform.engineer.company/da/portfolio/automated-data-processing-tasks-using-shell-scripting-pl-45/","title":"Har automatiseret databehandlingsopgaver med shell‑scripting, PL/pgSQL, Python og Transact‑SQL og øget produktiviteten og effektiviteten.","summary":"Automatiserede databehandling med Shell, PL/pgSQL, Python og Transact-SQL — højere produktivitet og slut med små, tilbagevendende fejl.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Der var en stabil mængde tilbagevendende databehandlingsarbejde, der blev lavet i hånden. Manuelt dataarbejde har to problemer på én gang: det æder tid, og det er inkonsistent — lav den samme opgave i hånden nok gange, og den bliver gjort en anelse forskelligt, og nogle af de forskelle er fejl.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Målet var at automatisere de opgaver, både for at få tiden tilbage og for at gøre dem pålidelige.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Databehandlingsarbejdet blev automatiseret på tværs af de databaser og systemer, det rørte, med hvad end der passede til jobbet — shell‑scripting til limen, PL/pgSQL og Transact‑SQL nede i databaserne, Python, hvor det krævede mere, end SQL kunne give. Manuelle trin blev erstattet med jobs, der kørte på samme måde hver gang, hvilket er hele pointen: et script bliver ikke træt, springer ikke et trin over og gør det ikke anderledes en fredag eftermiddag.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Produktiviteten og effektiviteten gik begge op, det manuelle arbejde kom af folks bord, og databehandlingen blev konsistent og pålidelig i stedet for en kilde til små, tilbagevendende fejl.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Data engineering","Databaser","Datapipelines (ETL/ELT)","PostgreSQL","Python","SQL","Databaseadministration (DBA)","DevOps \u0026 CI/CD‑automatisering","Udvikling af datapipelines (ETL/ELT)"]},{"id":"https://platform.engineer.company/da/portfolio/configured-and-deployed-1-000-wi-fi-routers-48/","url":"https://platform.engineer.company/da/portfolio/configured-and-deployed-1-000-wi-fi-routers-48/","title":"Har konfigureret og idriftsat 1.000 Wi‑Fi‑routere og forbedret netværkstilgængeligheden og -ydeevnen for kunderne.","summary":"Konfigurerede og idriftsatte 1.000 Wi-Fi-routere med standardopsætning — pålidelig trådløs adgang og ydeevne for kunderne.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Kunder havde brug for trådløst, der bare virkede, og det kom ned til at konfigurere og rulle et stort antal Wi‑Fi‑routere ud — og gøre det på den samme omhyggelige måde hver gang, for en router sat sjusket op er enten usikker eller langsom, og som regel finder man ud af hvilken en senere.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At konfigurere og udrulle de routere for at give kunderne bedre netværksadgang og -ydeevne var jobbet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Tusind Wi‑Fi‑routere blev konfigureret og udrullet. Kunsten ved det antal er at standardisere opsætningen — en konsistent, sikker, fornuftig konfiguration — frem for at tune hver enkelt fra bunden på dagen, for tusind håndlavede routere er tusind forskellige ting at supportere senere. Så de blev sat op for sikkerhed og ydeevne på samme måde hver gang og rullet pålideligt ud på tværs af kundelokationer.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e De tusind routere gav kunderne pålideligt trådløst — bedre adgang, bedre ydeevne — og gjorde det konsistent, fordi opsætningen var standard frem for improviseret. En router, ingen skal tænke over igen, er målet; de fleste af disse nåede dertil.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Drift \u0026 backup","Infrastruktur","Netværk \u0026 VPN","Sikkerhed","Systemadministration","IT‑support \u0026 helpdesk","Netværk \u0026 VPN‑opsætning"]},{"id":"https://platform.engineer.company/da/portfolio/administered-100-bare-bone-servers-physical-networks-and-50/","url":"https://platform.engineer.company/da/portfolio/administered-100-bare-bone-servers-physical-networks-and-50/","title":"Har administreret 100 barebone‑servere, fysiske netværk og IP‑telefonisystemer og sikret en robust infrastruktur til virksomhedens vækst.","summary":"Administrerede 100 barebone-servere, fysiske netværk og IP-telefoni — det robuste fundament, virksomheden voksede på.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Under alt, hvad virksomheden lavede, lå det fysiske — servere, de faktiske netværk, IP‑telefonien — og det skulle bare køre. Det lag er usynligt, når det virker, og ekstremt synligt i det øjeblik, det ikke gør, og virksomhedens vækst hvilede på, at det holdt sig pålideligt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At administrere den infrastruktur og holde den stabil, efterhånden som virksomheden voksede, var jobbet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Hundrede barebone‑servere sammen med de fysiske netværk og IP‑telefonisystemerne blev passet — opsætningen, vedligeholdelsen, fejlfindingen, når noget gik galt. Barebone‑servere betyder, at man har med hardwaren direkte at gøre, så der er en hands‑on, fysisk side af det: kablingen, kasserne, telefonsystemet, som alle bemærker i det sekund, et opkald falder. Jobbet var at holde det hele kedeligt, i den gode forstand.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Serverne, netværkene og telefonien kørte pålideligt, og det pålidelige fysiske fundament er det, der lod virksomheden blive ved med at vokse, uden at grunden flyttede sig under den.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Netværk \u0026 VPN","Systemadministration","Netværk \u0026 VPN‑opsætning"]},{"id":"https://platform.engineer.company/da/portfolio/administered-40-websites-on-ubuntu-linux-hosting-servers-53/","url":"https://platform.engineer.company/da/portfolio/administered-40-websites-on-ubuntu-linux-hosting-servers-53/","title":"Har administreret 40 websites på Ubuntu Linux‑hostingservere med Apache og Nginx og sikret høj tilgængelighed og ydeevne.","summary":"Administrerede 40 websites på Ubuntu Linux med Apache og Nginx — høj tilgængelighed og ydeevne, hosting man ikke behøvede tænke over.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Der var en portefølje af live websites, der skulle blive oppe og blive hurtige — og hosting er endnu et af de jobs, der er usynlige, indtil et site går ned, hvorefter det er det eneste, nogen bekymrer sig om.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At administrere de sites og holde dem højt tilgængelige og hurtige var jobbet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Fyrre websites kørte på Ubuntu Linux‑hostingservere, på en blanding af Apache og Nginx — konfigurationen, ydeevnetuningen, den løbende vedligeholdelse for at holde dem pålidelige under reel trafik. Reel trafik er det afgørende: et site, der er fint, når ingen bruger det, og vælter, når de gør, er ikke blevet administreret, det er bare blevet ladt i fred. Så arbejdet var at holde dem sunde under faktisk belastning.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Alle fyrre kørte med høj tilgængelighed og god ydeevne, hvilket gav kunderne hosting, de ikke behøvede at tænke over. Stabil og pålidelig under reel brug er hele pointen med hosting, og det er det, disse leverede.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Performanceoptimering","Systemadministration","Webudvikling","Site reliability \u0026 monitorering","Webstedsudvikling \u0026 CMS"]},{"id":"https://platform.engineer.company/da/portfolio/architected-developed-implemented-supported-infrastructure-data-processing-and-55/","url":"https://platform.engineer.company/da/portfolio/architected-developed-implemented-supported-infrastructure-data-processing-and-55/","title":"Arkitekt, udviklet, implementeret, understøttet infrastruktur, databehandling og kortapplikationen i 2 år uden pause, uden weekender, helligdage eller ferie, 10–14 timer om dagen.","summary":"Arkitekterede, byggede og driftede infrastruktur, databehandling og kortapp i to år uden pause — produktets pålidelige rygrad.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En green energy‑startup i en tidlig fase var afhængig af én platform til at spore, overvåge og optimere vedvarende energiaktiver, men havde hverken et dedikeret infrastrukturteam eller en etableret ingeniørorganisation til at bygge og drive den. Hele det tekniske fundament — cloud‑infrastruktur, datapipelines og den kundevendte GIS‑kortapplikation — skulle skabes og holdes kørende uafbrudt, på et marked hvor enhver nedetid eller datamangel direkte svækkede kundernes tillid og omsætningen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Opgaven var egenhændigt at arkitektere, bygge og drive hele systemet fra ende til anden — på tværs af platform- og dataingeniørarbejde, DevOps og site reliability. Ud over at skrive softwaren betød det at eje produktionsmiljøet: at provisionere og hærde infrastruktur, designe databehandlingslaget der fodrede kortet, og garantere at applikationen forblev tilgængelig døgnet rundt for en voksende kundebase — alt sammen inden for en hurtig startups rammer og ubønhørlige tempo.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e I to år blev infrastrukturen, datapipelines og kortapplikationen designet, implementeret og understøttet uden afbrydelse — uden weekender, helligdage eller ferie, ofte ti til fjorten timer om dagen. En pragmatisk, modulær arkitektur blev valgt for at holde en enmandsdrift vedligeholdelsesvenlig, med automatiseret provisionering, monitorering og alarmering, så problemer kunne opdages og løses hurtigt. Databehandlingen blev løbende optimeret for pålidelighed og ydeevne, releases blev udrullet trinvist, og hvert lag — fra servere til det brugervendte kort — blev personligt vedligeholdt og forbedret ud fra reel kundeanvendelse.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Platformen forblev konstant tilgængelig og udviklede sig fra en skrøbelig tidlig prototype til produktets pålidelige rygrad og bar virksomheden gennem dens kritiske vækstfase alene på styrken af én ingeniørs ejerskab. Denne praktiske forvaltning holdt infrastruktur, data og kortapplikation pålidelig nok til at understøtte mersalg, datalicensering og tiltrækning af nye kunder og demonstrerede en sjælden grad af engagement, bredde og ansvar fra ende til anden på tværs af hele stakken.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Data engineering","Datapipelines (ETL/ELT)","Drift \u0026 backup","Full stack‑udvikling","GIS / Geospatial","Infrastruktur","Platformarkitektur","Full stack‑produktudvikling","GIS \u0026 geospatiale løsninger","Platform- \u0026 løsningsarkitektur","Site reliability \u0026 monitorering","Udvikling af datapipelines (ETL/ELT)"]},{"id":"https://platform.engineer.company/da/portfolio/optimized-budget-costs-10-times-with-zero-loss-56/","url":"https://platform.engineer.company/da/portfolio/optimized-budget-costs-10-times-with-zero-loss-56/","title":"Har optimeret budgetomkostningerne 10 gange uden tab af produktivitet for den saudiarabiske virksomhed ved at nytænke den samlede infrastruktur, fjerne unødvendige tjenester og flytte væk fra AWS‑skyen.","summary":"Reducerede infrastrukturbudgettet 10× uden produktivitetstab for en saudiarabisk virksomhed ved at nytænke stacken og forlade AWS.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En virksomhed med base i Saudi‑Arabien slæbte rundt på slemt oppustede infrastrukturomkostninger. Deres AWS‑setup var blevet overdimensioneret og havde samlet tjenester, de ikke længere brugte, så cloud‑regningen var drevet fuldstændig ud af proportion med, hvad forretningen faktisk havde brug for. Det er en almindelig historie — ingen sætter sig for at overforbruge, det aflejrer sig bare, når ingen holder øje med måleren.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Opgaven var at skære omkostningerne betydeligt ned uden at miste produktivitet, hvilket betød at gentænke infrastrukturen ordentligt frem for at beskære i kanterne — kantbeskæring flytter sjældent en regning, der er strukturelt for stor.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Så arbejdet gik fra ende til anden. Først kom en audit af, hvad der faktisk blev brugt — hvilket er der, de duplikerede og unødvendige tjenester viser sig — og de blev skåret væk. Så blev det, der var tilbage, right‑sizet til at matche reel efterspørgsel i stedet for de worst‑case‑gæt, det oprindelige setup var bygget på. Og det store træk var at flytte workloads helt væk fra AWS, over på et mere omkostningseffektivt hosting‑arrangement — gjort omhyggeligt, i etaper, så den kørende forretning aldrig mærkede migreringen ske under sig.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Budgetomkostningerne faldt omkring ti gange, uden tab af produktivitet — den samme kapabilitet til en brøkdel af, hvad de havde betalt. Det frigjorde et reelt beløb, der stille og roligt var sivet ud i en overdimensioneret cloud‑regning måned efter måned, hvilket for forretningen var penge direkte tilbage på bundlinjen.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Cloud","DevOps","Infrastruktur","Løsningsarkitektur","Migrering \u0026 modernisering","Platformarkitektur","Cloud‑infrastruktur \u0026 migrering","DevOps \u0026 CI/CD‑automatisering","Platform- \u0026 løsningsarkitektur"]},{"id":"https://platform.engineer.company/da/portfolio/designed-an-organization-context-switching-system-with-client-59/","url":"https://platform.engineer.company/da/portfolio/designed-an-organization-context-switching-system-with-client-59/","title":"Har designet et system til at skifte organisationskontekst med client‑localStorage og server‑side cookie‑spejling, så brugere kan agere som administrerede organisationer under håndhævelse af least‑privilege‑autorisation.","summary":"Byggede organisationskontekst-skift med localStorage og server-cookie-spejling — brugere agerer som administrerede orgs under least-privilege.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e På NextMariner kan en maritim professionel administrere organisationer — virksomheder, akademier — der ikke har deres egne logins. Personen er kontoen; organisationen er noget, de agerer på vegne af. Så en bruger skal kunne bevæge sig gennem hele appen som enhver organisation, de administrerer, og skifte mellem dem frit, og den bekvemmelighed må ikke blive til et hul i autorisationen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Kontekstskift skulle være hurtigt og upåfaldende for brugeren og samtidig sikre, at den aktive kontekst aldrig af sig selv kunne give nogen adgang, de ikke var berettiget til.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e En OrganizationContext håndterer det, med lagring på begge sider. På klienten er localStorage kilde til sandhed for, hvilken organisation man aktuelt agerer som, så skiftet er øjeblikkeligt — ingen rundtur. En server‑side cookie spejler det, så server‑renderede sider løser den samme kontekst under SSR; der er en getServerViewMode på serveren, der læser den. Det vigtige er, at intet af det bliver stolet på til adgangsbeslutninger. Autorisation genkontrolleres på serveren ved hver request. Frontend‑konteksten er der for oplevelsen — at vise dig det rigtige — og serveren er den eneste autoritet på, hvad du må.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En bruger kan agere som enhver organisation, de administrerer, uden friktion, og grænsefladen holder sig i sync på både klient og server. Men fordi rettigheder verificeres server‑side hver gang, svækker intet af den bekvemmelighed sikkerheden. En, der pillede ved det, der ligger i localStorage, ændrer, hvad deres eget UI viser dem, og intet mere — serveren siger stadig nej.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Backend‑udvikling","Frontend‑udvikling","Full stack‑udvikling","Platformarkitektur","Sikkerhed","Backend- \u0026 API‑udvikling","Platform- \u0026 løsningsarkitektur","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/built-a-request-schema-validation-contract-with-automated-61/","url":"https://platform.engineer.company/da/portfolio/built-a-request-schema-validation-contract-with-automated-61/","title":"Har genereret API‑kontrakten udad fra databasen — OpenAPI, en typet TypeScript‑klient på 44.076 linjer, 61 mock handlers og de grænser, UI'et håndhæver — med en guard i hvert led, der fejler ved drift.","summary":"Genererede API-kontrakten ud fra databasen — OpenAPI, en typet TypeScript-klient, mock handlers og UI-grænser — med en guard i hvert eneste led.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Frontend og backend bevæger sig i deres eget tempo, og deres antagelser om en request‑payload kan glide fra hinanden, uden at nogen bemærker det. Måden, man som regel opdager det på, er et 422 i browseren — efter uoverensstemmelsen allerede er udgivet, hvilket er det dyreste tidspunkt at få det at vide på. At skrive de to sider i hånden ud fra det samme dokument løser det ikke; det flytter bare driften over på den, der glemte at læse dokumentet igen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e De to sider skulle genereres fra én kilde frem for aftales mellem to, med hvert trin i genereringen tjekket i stedet for taget for givet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Kæden starter ved databasen og løber udad. Skemaet og dets funktioner definerer formerne; Go‑typerne definerer API\u0026rsquo;et; Huma udsender OpenAPI‑beskrivelsen ud fra dem; en typet TypeScript‑klient — 44.076 linjer af den — genereres ud fra den beskrivelse; 61 mock‑handlere genereres ved siden af den, så frontendens egne tests kører mod den rigtige kontrakt frem for en håndskrevet fixture; og de grænser, UI\u0026rsquo;et håndhæver på en formular, kommer fra det samme sted i stedet for at blive tastet ind i en validator igen. Hvert hop har et værn. Et kontrakt‑tjek i CI sammenligner det, frontenden sender, med det, API\u0026rsquo;et forventer, og får buildet til at fejle ved afvigelse, med en schema‑probe under det, som tjekker de rigtige former frem for en beskrivelse af dem. Det dækker bevidst, hvor drift kan lide at gemme sig: valgfrie body‑felter, hvor \u0026ldquo;mangler\u0026rdquo; og \u0026ldquo;null\u0026rdquo; bliver forvekslet, og query‑parameter‑enums, hvor de to sider stille og roligt kan være uenige om de tilladte værdier.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Et felt kan ikke ændre sig på kun den ene side — det fejler ved det første hop, der bemærker det, i et build, minutter efter ændringen. Det tog en tilbagevendende og oprigtigt irriterende klasse af fejl af bordet — den slags, der er usynlig i code review og først dukker op i runtime. Prisen er et genereringstrin midt i det hele: at regenerere er en sur pligt, og kæden er kun så troværdig som sit dårligst bevogtede led, hvilket er grunden til, at hvert hop fik et.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Automatisering \u0026 CI/CD","Backend‑udvikling","DevOps","Drift \u0026 backup","Test \u0026 QA","Backend- \u0026 API‑udvikling","DevOps \u0026 CI/CD‑automatisering"]},{"id":"https://platform.engineer.company/da/portfolio/built-email-as-a-platform-capability-with-failover-62/","url":"https://platform.engineer.company/da/portfolio/built-email-as-a-platform-capability-with-failover-62/","title":"Har bygget e‑mail som en kapabilitet i platformen — tre udbydere med failover, delivery‑webhooks, logning af afsendelse og levering, templating og kampagner — bag et opstartstjek, der ikke booter uden en af dem.","summary":"Byggede e-mail som en kapabilitet i platformen: tre udbydere med failover, delivery-webhooks, logning af afsendelse og levering, templating og kampagner.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e E‑mail vejer tungt på NextMariner — verificering, notifikationer, digests, kampagner, de ting en bruger faktisk venter på. Og en mailudbyder er præcis den slags afhængighed, der fejler lydløst: konfigurationen ser fin ud, appen booter, og du opdager først, at noget er galt, når et rigtigt menneske aldrig får den besked, det blev lovet. Det er den værste måde at få det at vide på. Én udbyder gør det værre, fordi fejlen er total og en andens at rette.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e E‑mail skulle behandles som en evne, platformen ejer, frem for et klientbibliotek, den kalder — i stand til at overleve et udbyder‑nedbrud, i stand til at sige, hvad der skete med en given besked, og højlydt ved opstart i de miljøer, hvor stilhed er farlig.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Tre udbydere sidder bag én grænseflade — SendGrid som primær, med SMTP2GO og Azure Communication Services bagved — og failover mellem dem er automatisk frem for en konfigurationsændring foretaget under pres. Levering tages ikke for givet: indgående webhooks rapporterer, hvad hver udbyder gjorde med en besked, og begge sider registreres, i en send‑log og en delivery event‑tabel, så \u0026ldquo;fik det her menneske sin verificeringsmail\u0026rdquo; er en forespørgsel frem for et gæt. Templating holder beskedteksterne ude af koden, og et separat broadcast‑skema — 5 tabeller og 24 funktioner — bærer kampagner ud til segmenter af brugere, hvilket er et andet problem end transaktionsmail og blev bygget som et. Foran det hele sender et opstartstjek en rigtig besked gennem stakken, bag et flag: i udvikling logger det en advarsel og fortsætter, for ingen vil have deres laptop til at nægte at starte, fordi en sandkasse‑nøgle er udløbet, og i staging og produktion er en fejl fatal, og processen afslutter frem for at deploye et build, der ikke kan sende mail. Selve afsendelsesstien går gennem en SSRF‑beskyttet klient med et 30‑sekunders timeout, og den asynkrone leveringssti har retries og backoff, så et øjebliks udfald ikke taber en besked.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En hel kategori af lydløs fejl flyttede fra \u0026ldquo;en bruger opdager det dage senere\u0026rdquo; til \u0026ldquo;deployet stopper\u0026rdquo;, og en udbyder, der har en dårlig eftermiddag, blev til en forringet sti frem for et udfald. Prisen er tre integrationer at holde kørende i stedet for én, og leveringslogs, der vokser og skal beskæres — begge accepteret, fordi e‑mail er den kanal, platformen ikke kan rute uden om.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Backend‑udvikling","Cloud","Drift \u0026 backup","Sikkerhed","Backend- \u0026 API‑udvikling","Sikkerhed \u0026 adgangsstyring","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/provisioned-azure-infrastructure-as-code-with-bicep-container-68/","url":"https://platform.engineer.company/da/portfolio/provisioned-azure-infrastructure-as-code-with-bicep-container-68/","title":"Har provisioneret Azure‑infrastruktur som kode med Bicep — Container Apps, PostgreSQL Flexible Server, Front Door/WAF og netværk — på tværs af udviklings-, staging- og produktionsmiljøer.","summary":"Provisionerede Azure-infrastruktur som kode med Bicep — Container Apps, PostgreSQL, Front Door/WAF — reproducerbar på tværs af miljøer.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e NextMariner lever på Azure, og Azure lavet i hånden — klik gennem portalen, en indstilling justeret her og der — er en fælde. Det driver, ingen husker hvorfor noget er, som det er, og at genopbygge det efter en dårlig dag er langsomt og nervepirrende. Med mere end ét miljø at holde i takt bliver det kun værre.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Læg det hele i kode, så et miljø er noget, man kan læse, reviewe og genskabe frem for en bunke manuel tilstand.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Estatet er defineret i Bicep. Hvert miljø — testing, staging, product — kommer ud af de samme templates: api\u0026rsquo;et og www kørende som Azure Container Apps på et managed environment, en PostgreSQL Flexible Server, Redis til caching, Front Door med en WAF‑politik ude foran og netværket under det (VNet, NSG, private DNS), med Log Analytics koblet på til diagnostik. Images hentes fra projektets Azure Container Registry. Fordi det hele er parametriseret, er det at rejse et nyt miljø eller ændre et eksisterende en pull request, ikke en supportsag til en selv.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Miljøerne blev reproducerbare og reviderbare. Drift holdt op med at være et mysterium, fordi kilden til sandhed er koden, og at bringe infrastruktur op eller tilbage er et spørgsmål om at anvende templates frem for at huske, hvad der blev klikket sidst. Det er forskellen mellem infrastruktur, man ejer, og infrastruktur, der ejer én.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Cloud","DevOps","Drift \u0026 backup","Infrastruktur","Netværk \u0026 VPN","Platformarkitektur","Sikkerhed","Cloud‑infrastruktur \u0026 migrering","Infrastructure as Code","Netværk \u0026 VPN‑opsætning","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/built-github-actions-ci-cd-pipelines-with-a-69/","url":"https://platform.engineer.company/da/portfolio/built-github-actions-ci-cd-pipelines-with-a-69/","title":"Har bygget GitHub Actions CI/CD‑pipelines med et distroless produktions‑frontend‑image og promovering på tværs af flere miljøer.","summary":"Byggede GitHub Actions CI/CD med et distroless produktions-frontend-image og promovering på tværs af miljøer — rutinemæssige releases.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Levering bør ikke afhænge af, at nogen husker trinene, og det, der ender med at køre i produktion, bør ikke være en fed general‑purpose‑container, der bærer en shell og en package manager, den aldrig vil bruge — det er bare angrebsflade, der sidder der uden grund.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Gør vejen fra commit til kørende‑i‑Azure automatisk, og hold produktions‑images så små og låst ned, som hver workload tillader.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Pipelinen er GitHub Actions. Separate workflows håndterer kodekvalitets‑gaten, testene og de miljøspecifikke deploys, med CodeQL, dependency review og et SBOM‑trin ved siden af, så intet når et miljø uden først at passere tjekkene. Images er multi‑stage‑builds, og basen for hver del blev valgt efter dens fortjeneste frem for ét blankt valg: frontenden sendes på et distroless image (gcr.io/distroless/cc‑debian13 — ingen shell, ingen package manager), Go‑API\u0026rsquo;et på en slank Alpine og database‑imaget på postgres‑slim. Promovering flytter et build gennem miljøerne ad en defineret rute frem for i hånden.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Releases holdt op med at være et omhyggeligt manuelt ritual og blev en rutinemæssig, kedelig hændelse, hvilket er præcis, hvad man vil have fra releases. Produktions‑frontenden kører på omtrent så lidt, som man kan give den, tjekkene fanger problemer, før de lander, og \u0026ldquo;deploy\u0026rdquo; er noget, pipelinen gør, frem for noget, nogen sveder sig igennem.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Cloud","Containere (Docker/Kubernetes)","DevOps","Drift \u0026 backup","Sikkerhed","Test \u0026 QA","Containerisering \u0026 orkestrering","DevOps \u0026 CI/CD‑automatisering"]},{"id":"https://platform.engineer.company/da/portfolio/authored-578-go-task-automation-targets-70/","url":"https://platform.engineer.company/da/portfolio/authored-578-go-task-automation-targets-70/","title":"Har skrevet 578 go‑task‑automatiseringsmål på tværs af native-, Docker- og HTTPS‑udviklingstilstande, linting, test, database og deployment.","summary":"Skrev 578 go-task-mål på tværs af native-, Docker- og HTTPS-tilstande — linting, test, database og deployment i én værktøjskæde.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e NextMariner er et polyglot‑monorepo — Go, TypeScript, SQL, Python, shell — og hvert af dem medbringer sin egen måde at bygge, teste, linte og køre på. Overladt til sig selv betyder det, at alle går rundt med et mentalt spikseddel af værktøjsspecifikke kommandoer, og at nytilkomne bruger deres første dag på bare at finde ud af, hvordan man får tingene til at køre.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Giv hele projektet én hoveddør: én konsistent måde at køre hvad som helst på, uanset hvilket sprog det tilfældigvis er skrevet i.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Det er bygget ud med go‑task — et Taskfile‑lag, der er vokset til 578 navngivne mål, 50 i rodfilen og 528 i namespacede filer under den. Der er udviklingstilstandene (native, Docker, en HTTPS‑variant til at teste PWA og mobil), kodekvalitetssiden (lint, format, test, fix på tværs af alle sprogene), databasehåndtering og de miljøspecifikke build- og deploy‑opgaver. Der er endda en low‑memory‑tilstand til maskiner, der ikke kan undvære den RAM, det kræver at bygge frontenden på den sædvanlige måde. Pointen var aldrig at have mange tasks; den var, at man aldrig behøver at kende den underliggende kommando.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Enhver kan køre task \u0026ndash;list og se hele værktøjskæden lagt frem og køre en hvilken som helst del af den på samme måde uanset, hvad der er under motorhjelmen. Onboarding blev kortere, og de små, dumme fejl — forkert flag, forkert mappe, halvt husket kommando — forsvandt for det meste. Tallet er også en advarsel: 578 mål er forbi, hvad nogen kan holde i hovedet, så navngivningen og namespacingen er det, der holder det brugbart, frem for at antallet er noget at være stolt af.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Teknisk ledelse","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Teknisk dokumentation"]},{"id":"https://platform.engineer.company/da/portfolio/owned-end-to-end-deployments-of-the-platform-71/","url":"https://platform.engineer.company/da/portfolio/owned-end-to-end-deployments-of-the-platform-71/","title":"Har haft ansvaret for end‑to‑end‑deployments af platformen til Azure og styret releases på tværs af udviklings-, staging- og produktionsmiljøer.","summary":"Havde ansvaret for end-to-end Azure-deployments — releases gennem dev, staging og produktion ad en defineret, gentagelig vej.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Platformen skulle nå brugerne på tværs af flere Azure‑miljøer, og deployment er den søm, hvor infrastrukturen, build‑pipelinen og applikationen alle mødes. Det er også der, hvor en lille fejl holder op med at være en bug og bliver til et nedbrud, så det er den del, man mindst af alt vil lave i hånden og halvt efter hukommelsen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Deployment blev ejet fra ende til anden, så en ændring flyttede ud til hvert miljø på den samme forudsigelige måde hver gang.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Releases bevæger sig ad en fast rute — udvikling, så staging, så produktion — frem for at nogen pusher direkte til et live miljø. CI/CD‑pipelinen bygger images og sender dem, og Bicep‑templates holder målinfrastrukturen ens fra det ene miljø til det næste, så et build ikke promoveres ind i et lidt anderledes sted hver gang. Konfiguration, der er forskellig pr. miljø, holdes adskilt fra secrets, hvilket betyder, at det samme byggede artefakt kan promoveres gennem miljøerne og bare samler de rette indstillinger op, hvor det lander, i stedet for at blive bygget om for hvert.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Ændringer når hvert miljø forudsigeligt, ad en defineret vej, uden ad hoc‑manuelle deploys i blandingen. At release blev til et kontrolleret, gentageligt trin i stedet for et med tilbageholdt vejrtrækning, og det er en stor del af, hvad der holdt den live platform stabil, mens den stadig ændrede sig hurtigt nedenunder.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Cloud","DevOps","Drift \u0026 backup","Infrastruktur","Sikkerhed","Cloud‑infrastruktur \u0026 migrering","DevOps \u0026 CI/CD‑automatisering","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/configured-database-backup-retention-as-infrastructure-as-code-72/","url":"https://platform.engineer.company/da/portfolio/configured-database-backup-retention-as-infrastructure-as-code-72/","title":"Har konfigureret opbevaring af databasebackups som infrastructure‑as‑code, gennemgået genopretningsberedskabet og dokumenteret restore‑proceduren — med de resterende huller navngivet frem for fundet under en hændelse.","summary":"Implementerede databasebackups og en disaster recovery-strategi via infrastructure-as-code — hurtig, reproducerbar genopretning.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Et produkt, der lever på sine data, har ikke råd til at miste nogen, og \u0026ldquo;der er backups et sted\u0026rdquo; er et håb, ikke en genopretningsplan. Den eneste backup, der er noget værd, er en, man ved gendanner, ind i et miljø, man ved, man kan genopbygge. NextMariner havde ingen af de to halvdele skrevet ned.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Få den genoprettelige position defineret i stedet for antaget — dataene, miljøet omkring dem, og et ærligt billede af, hvor langt det faktisk rækker i dag.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Backup‑retention er konfigureret i Bicep ved siden af den database, den beskytter, så gendannelse til et tidspunkt er en egenskab ved templaten frem for en indstilling, nogen engang klikkede i en portal. Miljøet omkring den er også defineret som infrastructure‑as‑code, hvilket er den stille halvdel, folk glemmer: at gendanne en database ind i et miljø, man skulle genopbygge i hånden efter hukommelsen, er ikke rigtig genopretning. Derefter blev positionen gennemgået og skrevet op — gendannelsesproceduren, den øvelse, der ville måle den, og de huller, der stadig står åbne: geo‑redundans er slået fra, og målet for genopretningstid er foreslået frem for målt, fordi ingen øvelse er kørt endnu.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Genopretning holdt op med at være en vag beroligelse og blev en dokumenteret position med sine huller navngivet. Det lyder mindre imponerende end \u0026ldquo;disaster recovery: klaret\u0026rdquo;, og det er væsentligt mere værd — den næste, der rører ved det, ved, hvad der er dækket, hvad der ikke er, og præcis hvilken øvelse der lukker forskellen. Et navngivet hul kan man lukke; et unavngivet bliver opdaget under en hændelse.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Cloud","Databaser","DevOps","Drift \u0026 backup","Infrastruktur","PostgreSQL","Backup \u0026 disaster recovery","Databaseadministration (DBA)","Infrastructure as Code"]},{"id":"https://platform.engineer.company/da/portfolio/hardened-the-application-with-nonce-based-csp-hsts-77/","url":"https://platform.engineer.company/da/portfolio/hardened-the-application-with-nonce-based-csp-hsts-77/","title":"Har hærdet applikationen med nonce‑baseret CSP, HSTS, SameSite‑cookies, least‑privilege‑databaseroller og server‑side‑genkontrol af rettigheder.","summary":"Hærdede appen med nonce-baseret CSP, HSTS, SameSite-cookies, least-privilege-databaseroller og server-side-genkontrol af rettigheder.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e NextMariner rummer professionelle og organisatoriske data, den slags folk forventer bliver håndteret ordentligt, så en enkelt forsvarslinje var aldrig nok. Arbejdsantagelsen må være, at klienten er fjendtlig — at alt, hvad browseren håndhæver, kan slås fra af den, der holder browseren — og sikkerheden må holde alligevel.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Platformen skulle hærdes på hvert lag — frontend, API, database — så sikkerhed blev håndhævet af serveren uafhængigt af, hvad grænsefladen nu tilfældigvis tillod.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e I frontenden sætter Next.js\u0026rsquo; proxy‑middleware — proxy.ts — en Content‑Security‑Policy med en per‑request‑nonce og strict‑dynamic, plus HSTS og SameSite‑cookies, så browseren er låst ned om, hvad den vil køre og sende. På API\u0026rsquo;et er der rate limiting, CORS, request‑størrelsesgrænser, input‑validering, før noget rører databasen, og logning af de sikkerhedsrelevante hændelser. I databasen logger API\u0026rsquo;et ind som en least‑privilege‑rolle, der kun kan EXECUTE app‑funktionerne, funktionerne kører SECURITY DEFINER, og alt er parametriseret. Og rettighederne — tier, rolle, organisation, skibs‑scoping — genkontrolleres på serveren ved hver request, hvor frontend‑gates kun behandles som UX. Gates afgør, hvad du ser; serveren afgør, hvad du må.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Sikkerhed afhænger ikke af, at UI\u0026rsquo;et opfører sig. Beskyttelserne er lagdelte, så det at komme forbi én ikke får dig forbi resten, og det hele er bygget på antagelsen om, at klienten ikke kan stoles på — hvilket er den rigtige antagelse for data, folk overlader i fortrolighed.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Backend‑udvikling","Databaser","Frontend‑udvikling","Platformarkitektur","Sikkerhed","Backend- \u0026 API‑udvikling","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/set-a-zero-warnings-quality-bar-across-six-79/","url":"https://platform.engineer.company/da/portfolio/set-a-zero-warnings-quality-bar-across-six-79/","title":"Har sat en zero‑warnings‑kvalitetsstandard på tværs af seks sprog — Go, TypeScript, SQL, Python, Shell og Markdown — håndhævet af pre‑commit‑hooks.","summary":"Satte en zero-warnings-kvalitetsstandard på tværs af Go, TypeScript, SQL, Python, Shell og Markdown — håndhævet af pre-commit-hooks.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Advarsler, der hober sig op, er stille og roligt ætsende. Hver diagnostik, man ignorerer, sænker barren en smule, og når først buildet spytter fyrre af dem ud, læser ingen nogen af dem, og et reelt problem sidder i den liste i fuldt dagslys, fordi \u0026ldquo;advarsler\u0026rdquo; er blevet til baggrundsstøj. I et polyglot‑kodebase er der så mange flere kilder til støj til at lade det ske.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Repoet skulle have én kompromisløs kvalitetsstandard på tværs af hvert sprog, så ting blev rettet i stedet for at hobe sig op.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e En zero‑warnings‑politik gik ind, med værktøjerne som det, der håndhæver den, for en politik, der bygger på alles årvågenhed, taber til den første travle uge. Hver linter‑diagnostik er en fejl — der er intet \u0026ldquo;warn\u0026rdquo;-niveau at gemme sig i — og det er det samme på tværs af hele stakken: Go med golangci‑lint, TypeScript med ESLint, SQL med SQLFluff, Python med Ruff, shell med ShellCheck, Markdown med markdownlint. Inline‑suppressions er forbudt, så man kan ikke papre hen over en diagnostik; man er nødt til faktisk at rette tingen. Pre‑commit- og pre‑push‑hooks kører linterne og testene, så en commit, der ville introducere et problem, slet ikke bliver lavet i første omgang. Der er endda grænser for funktions- og fillængde for at holde moduler fra at brede sig ud over det punkt, hvor de er læsbare.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Problemer bliver rettet ved kilden i stedet for udskudt til en backlog, ingen rydder, og kodebasen forbliver ren som standard frem for ved periodiske heltemodige indsatser. Standarden er identisk, uanset hvilket sprog man er i, og det er værktøjerne, der holder den — ikke nogens viljestyrke — hvilket er derfor, den faktisk holder.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Teknisk ledelse","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/provided-round-the-clock-24-7-infrastructure-support-82/","url":"https://platform.engineer.company/da/portfolio/provided-round-the-clock-24-7-infrastructure-support-82/","title":"Har leveret døgnbemandet 24/7‑infrastruktursupport til en IPTV/OTT‑streamingplatform og administreret ca. 1.000 servere samt kundeejede systemer for globale kunder i Kina, USA og Tyskland.","summary":"Leverede 24/7-infrastruktursupport til en IPTV/OTT-streamingplatform — ~1.000 servere plus kundesystemer i Kina, USA og Tyskland.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Det her var en IPTV/OTT‑streamingplatform med kunder spredt over Kina, USA og Tyskland, hvilket betød, at der ikke var nogen stille time at lave vedligeholdelse i — nogen, et eller andet sted, kiggede altid med. Nedetid på sådan en platform er ikke en abstrakt måltal; det er nogens fjernsyn, der bare stopper, og de er ligeglade med hvorfor.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At holde platformens infrastruktur tilgængelig døgnet rundt var jobbet — reelt døgnet rundt, ikke \u0026ldquo;kontortid plus en tilkaldevagt, ingen svarer på.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e 24/7‑support kørte for omkring tusind servere, plus et pænt antal kundeejede systemer oveni — administreret, overvåget, holdt sikret og konfigureret på tværs af hele streaming‑estatet. Fordi kunderne sad i tre vidt forskellige tidszoner, fandtes \u0026ldquo;efter arbejdstid\u0026rdquo; ikke rigtig; et problem klokken 3 om natten lokalt var primetime for nogen andre, så det blev behandlet som primetime. En stor del af arbejdet var at bemærke noget, der drev af, før det blev til et nedbrud, for på en live streamingplatform får man ikke lov at rette ting stille bagefter.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Platformen forblev kontinuerligt tilgængelig for et globalt publikum, med problemer fanget og håndteret på hvilken time end de dukkede op, før de nåede en seers skærm. På en 24/7‑tjeneste er det hele jobbet — succes ser ud som ingenting, der sker, hvilket er præcis, hvad seerne ville have.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Cloud","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Monitorering \u0026 observability","Netværk \u0026 VPN","Systemadministration","IT‑support \u0026 helpdesk","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/ensured-uninterrupted-delivery-of-iptv-streaming-signals-between-83/","url":"https://platform.engineer.company/da/portfolio/ensured-uninterrupted-delivery-of-iptv-streaming-signals-between-83/","title":"Har sikret uafbrudt levering af IPTV‑streamingsignaler mellem leverandører og kunder og overvåget og vedligeholdt streamingnetværket og IP‑telefonien døgnet rundt.","summary":"Sikrede uafbrudt IPTV-streaming mellem leverandører og kunder — overvågede og vedligeholdt streamingnetværk og IP-telefoni døgnet rundt.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e IPTV lever eller dør på, at signalet kommer igennem. Streamsene flyder fra leverandører, gennem platformen, til kunderne, og ethvert brud hvor som helst i den kæde er en sort skærm for nogen. IP‑telefonien sad ved siden af, med det samme krav: den skulle bare virke.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At garantere, at signalleveringen og telefonien forblev uafbrudt, var jobbet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Streamingnetværket og IP‑telefonien blev overvåget, fejlfundet og vedligeholdt døgnet rundt. Pointen med at holde konstant øje er, at streamingproblemer melder sig som forringelse, før de bliver til et decideret drop — en stream, der begynder at hakke, et link, der bliver ustabilt — og hvis man er opmærksom, kan man fange det ved hakket i stedet for ved den sorte skærm. Så en stor del af det var at være foran signalet frem for at reagere på klager over det.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Streamsene og opkaldene forblev pålidelige på tværs af platformen, med problemer opdaget og rettet, før de blev til en tjeneste, nogen bemærkede faldt ud. At holde et signal flydende mellem leverandører og slutkunder uden et synligt hul er stille, konstant arbejde, og stille er, hvad det bør være.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Monitorering \u0026 observability","Netværk \u0026 VPN","Systemadministration","Netværk \u0026 VPN‑opsætning","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/planned-and-implemented-new-infrastructure-functionality-for-internal-85/","url":"https://platform.engineer.company/da/portfolio/planned-and-implemented-new-infrastructure-functionality-for-internal-85/","title":"Har planlagt og implementeret ny infrastrukturfunktionalitet til interne og eksterne systemer og bygget løsninger, der stadig kører år efter med minimale ændringer.","summary":"Planlagde og byggede infrastruktur til interne og eksterne systemer — robust nok til stadig at køre år efter med minimale ændringer.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Efterhånden som organisationen voksede, blev dens interne og eksterne systemer ved med at have brug for nye funktioner skruet på. Den nemme måde at gøre det på er, hvad end der er hurtigst i dag; problemet med den nemme måde er, at man er tilbage og retter det om et halvt år.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At planlægge og bygge infrastrukturfunktionalitet, der faktisk ville holde, var opgaven — ikke bare virke nu, men blive ved med at virke.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Ny infrastrukturfunktionalitet blev planlagt og implementeret på tværs af de interne og eksterne systemer, designet til at være holdbar — den slags, man bygger én gang, ordentligt, så den bliver ved med at køre i årevis med minimal berøring frem for at kræve konstant opmærksomhed. Det er et bevidst valg hver gang: at bruge en smule mere tanke i starten, så man ikke skriver sig op til at passe på den for evigt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Systemerne forblev funktionelle og effektive længe efter, de blev bygget, og kørte i årevis med næsten ingen ændringer. Den lang levetid er det egentlige mål for infrastrukturarbejde — enhver kan lave noget, der virker i dag; at lave noget, der stadig stille og roligt virker år senere, er det sværere og mere nyttige.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Drift \u0026 backup","Infrastruktur","Løsningsarkitektur","Platformarkitektur","Systemadministration","Platform- \u0026 løsningsarkitektur","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/as-one-of-the-first-hires-designed-and-86/","url":"https://platform.engineer.company/da/portfolio/as-one-of-the-first-hires-designed-and-86/","title":"Har som en af de første medarbejdere designet og bygget hele kerneinfrastrukturen og de understøttende processer fra bunden for en grøn‑energi‑SaaS‑startup og lagt fundamentet for hurtig vækst.","summary":"Designede og byggede som en af de første medarbejdere hele kerneinfrastrukturen fra bunden for en grøn-energi-SaaS-startup — grundlag for vækst.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Det her var en grøn‑energi‑SaaS‑startup med en lovende idé og reelt ingen teknisk fundament under sig endnu. At komme ind som en af de første medarbejdere betød det stadie, hvor der ikke er noget at vedligeholde, fordi der ikke findes noget — man bygger den grund, alle andre skal stå på.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At bygge kerneinfrastrukturen og processerne omkring den, fra bunden, var jobbet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Hele kerneinfrastrukturen og dens understøttende processer blev designet og bygget — serverne, netværkene, dataflowet, sikkerheden, driftssiden. At gøre det i en startup betyder at træffe beslutninger, der er svære at omgøre senere, så målet var ikke bare \u0026ldquo;få noget til at køre\u0026rdquo;, det var at lægge et fundament, der kunne bære vægten af hurtig vækst uden at skulle rives ud i det øjeblik, virksomheden blev større. Tidlige infrastrukturvalg bliver enten det, der lader dig skalere, eller det, du bruger et år på at gøre om; sigtet var klart den første slags.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Startuppen kom ud med et solidt teknisk fundament, og det er det, der lod forretningen vokse hurtigt bagefter. At være den, der bygger den base fra ingenting, er en særlig slags ansvar — gør du det rigtigt, bemærker ingen det, gør du det forkert, bemærker alle det — og denne holdt.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Cloud","DevOps","Infrastruktur","Løsningsarkitektur","Platformarkitektur","Sikkerhed","Systemadministration","Cloud‑infrastruktur \u0026 migrering","DevOps \u0026 CI/CD‑automatisering","Platform- \u0026 løsningsarkitektur"]},{"id":"https://platform.engineer.company/da/portfolio/automated-team-collaboration-password-management-task-and-time-89/","url":"https://platform.engineer.company/da/portfolio/automated-team-collaboration-password-management-task-and-time-89/","title":"Har automatiseret teamsamarbejde, adgangskodehåndtering, opgave- og tidsstyring og bygget et semi‑automatisk projektvisningssystem, hvilket øgede teamets produktivitet.","summary":"Automatiserede samarbejde, adgangskode-, opgave- og tidsstyring og byggede et semi-automatisk projektvisningssystem — højere produktivitet.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En stor del af teamets driftsarbejde — at koordinere, håndtere adgangskoder, tracke opgaver og tid — blev lavet i hånden, og manuel koordinering er en stille skat: det er aldrig det, man bemærker, men det æder støt timer, der kunne gå et bedre sted hen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At automatisere det gentagne driftsarbejde var målet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e De dele, der egnede sig til det, blev automatiseret — teamsamarbejde, adgangskodehåndtering, opgavestyring, tidsstyring — med et semi‑automatisk projektvisningssystem oveni. Idéen på tværs af det hele var at tage rutinekoordineringen af folks bord, så den kørte af sig selv, og lade dem bruge den genvundne opmærksomhed på arbejde, der faktisk krævede et menneske. Visningssystemet var det samme instinkt anvendt på noget mere synligt: at gøre det at præsentere arbejdet mest muligt automatisk frem for en manuel pligt hver gang.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Driftseffektiviteten steg, og teamet blev mere produktivt, fordi den rutinekoordinering, der før krævede konstant menneskelig opmærksomhed, nu i vid udstrækning kørte af sig selv. Den tid, der lækkede ud i pseudoarbejde, gik tilbage i det faktiske arbejde.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Projektledelse","Sikkerhed","Teknisk ledelse","DevOps \u0026 CI/CD‑automatisering","Projektledelse (Agile)","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/integrated-a-company-wide-password-management-system-strengthening-90/","url":"https://platform.engineer.company/da/portfolio/integrated-a-company-wide-password-management-system-strengthening-90/","title":"Har integreret et virksomhedsdækkende adgangskodehåndteringssystem, der styrkede sikkerheden og strømlinede adgangskontrollen.","summary":"Integrerede et virksomhedsdækkende adgangskodehåndteringssystem — stærkere sikkerhed og strømlinet adgangskontrol i hele organisationen.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Adgangsoplysninger blev håndteret inkonsistent — forskellige folk lagrede og delte dem på forskellige, ad hoc‑måder — og den inkonsistens er i sig selv sikkerhedsrisikoen. Det er sjældent et dramatisk brud; det er en adgangskode i en chatbesked, et delt login, ingen roterer, den langsomme ophobning af små eksponeringer.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At centralisere oplysningerne og gøre dem sikre var opgaven.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Et virksomhedsdækkende adgangskodehåndteringssystem gik ind, så der var én konsistent, sikker måde, oplysninger blev lagret og delt på, i stedet for hver enkelts personlige vane. Værdien af virksomhedsdækkende er præcis, at det ikke er valgfrit pr. person — en adgangskodehåndtering, kun halvdelen af teamet bruger, hjælper knap nok, for risikoen bor i den halvdel, der ikke gjorde. Så pointen var at gøre den sikre måde til standardmåden, overalt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Sikkerheden blev bedre, og adgangskontrollen blev enklere og mere konsistent i hele organisationen. Når først oplysningerne alle bor ét administreret sted, holder et helt sæt små, kedelige eksponeringer bare op med at være mulige — hvilket er det meste af, hvad sikkerhed i den virkelige verden faktisk er.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Infrastruktur","Sikkerhed","Systemadministration","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/built-a-layered-automated-test-suite-across-four-layers-94/","url":"https://platform.engineer.company/da/portfolio/built-a-layered-automated-test-suite-across-four-layers-94/","title":"Har bygget en lagdelt automatiseret testsuite — 981 Go‑tests, 543 frontend- og browserspecs, 494 SQL‑adfærdstests — med mutationstest, property‑based tests og en tilgængelighedsgate.","summary":"Byggede en lagdelt testsuite — 981 Go-tests, 543 frontend-specs og 494 SQL-adfærdstests — med gates for mutation, property-based test og tilgængelighed.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En platform, der holder sin forretningslogik i databasen, har et testproblem, de fleste projekter ikke har. Logikken er ikke i det sprog, testframeworket er godt til — den er i SQL, bag funktionsgrænser, og SQL er præcis den slags kode, der ender utestet, fordi det er akavet at teste. Læg et Go‑API og en Next.js‑frontend oven på det, og \u0026ldquo;de vigtige dele er dækket\u0026rdquo; bliver stille og roligt til \u0026ldquo;de dele, der var nemme at dække, er dækket.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Hvert lag skulle have sin adfærd kontrolleret dér, hvor adfærden faktisk bor, frem for at alt blev kontrolleret udefra gennem en browser.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Fire lag fik fire slags test. Go‑API\u0026rsquo;et bærer 981 testfunktioner fordelt på 310 filer. Frontenden bærer 543 Vitest- og Playwright‑specs, heraf 48 end‑to‑end‑filer og 30 browser‑specs. Databasen bærer 494 adfærdstestfiler — 116.876 linjer SQL, der tjekker sine antagelser gennem 6.654 rejste exceptions — så en stored function bliver testet i databasen frem for gennem tre lag applikation ovenover. Over dem ligger de test, der tester testene: Stryker mutation testing ødelægger med vilje en linje kode og fejler, når intet opdager det, og fast‑check genererer input, ingen tænkte på at skrive ned. En axe‑core‑gate kræver nul WCAG 2.0- og 2.1‑overtrædelser på niveau A og AA, hvilket gør tilgængelighed til noget, der får buildet til at fejle, frem for et revisionsfund måneder senere. Coverage‑tærskler bevæger sig kun opad. Hele suiten kører som 10 jobs i et 612‑linjers workflow.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e At ændre noget strukturelt holdt op med at være skræmmende, og det er det eneste, der holder et kodebase af den størrelse fra at forkalke. Den ærlige omkostning er tid — suiten er langsom, den beskatter hver eneste ændring, og i den størrelse skal den selv vedligeholdes. Det, den køber, er evnen til at blive ved med at bevæge sig hurtigt, og det er mere værd end de minutter, det tager.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Backend‑udvikling","DevOps","Drift \u0026 backup","Frontend‑udvikling","PostgreSQL","Test \u0026 QA","Backend- \u0026 API‑udvikling","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/built-the-repository-s-guard-engine-of-268-checks-95/","url":"https://platform.engineer.company/da/portfolio/built-the-repository-s-guard-engine-of-268-checks-95/","title":"Har bygget kodebasens guard‑motor — 268 registrerede commit‑tjek, 277 lint‑regler og 15 egne ESLint‑regler — plus 146 tests af selve tjekkene, så det er builden, der holder standarden, ikke reviewet.","summary":"Byggede en guard-motor med 268 registrerede commit-tjek, 277 lint-regler og 15 egne ESLint-regler — plus 146 tests af selve tjekkene.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Standarder, der er skrevet ned i en contributing‑guide, er forslag. Alle er enige i dem, og så er det fredag, ændringen er lille, og guiden taber. Zero‑warnings‑barren ville kun nogensinde holde, hvis noget andet end velvilje holdt den — og de lintere, der følger med hvert sprog, når slet ikke frem til de projektspecifikke regler, der faktisk betyder noget, dem om, hvordan netop dette kodebase er ment at virke.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e De regler, projektet gik op i, skulle kunne eksekveres, så det at bryde en fik en commit til at fejle frem for at vente på en reviewer med tid og hukommelse nok til at fange det.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Det, der voksede ud af det, er en guard‑motor. Der er 274 check‑scripts, 268 af dem registreret i commit‑hooks, ved siden af 277 JavaScript‑lintere og 96 shell- og 17 Python‑validatorer, der dækker de ting, hyldevareværktøj ikke har nogen mening om — at en migration kan rulles tilbage, at en oversættelsesnøgle findes i begge locales, at en registreret rute optræder i OpenAPI‑beskrivelsen, at ingen stille og roligt har tilføjet en inline‑suppression. Femten custom ESLint‑regler holder husets mønstre på plads i TypeScript. Én regel er værd at nævne for sig: enhver commit med præfikset fix: skal bære en test, der fejler uden den, så en fejl, der er rettet, bliver ved med at være rettet. Og fordi en ødelagt guard er værre end slet ingen guard — den lader alt passere, og ingen opdager det — har selve guard‑laget 146 test.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Review‑tiden flyttede fra mekanik til design, fordi de mekaniske indvendinger allerede var fremsat af en maskine, før branchen blev pushet. Trade‑off\u0026rsquo;et er reelt og værd at sige højt: det er langsomt at committe, og en dårligt skrevet guard er oprigtigt irriterende at arbejde uden om. De 146 test findes, fordi netop det skete.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Teknisk ledelse","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Teknisk dokumentation","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/built-the-payments-and-entitlements-layer-96/","url":"https://platform.engineer.company/da/portfolio/built-the-payments-and-entitlements-layer-96/","title":"Har bygget betalings- og rettighedslaget — Stripe side om side med Apple og Google in‑app purchase — så directory, søgning og eksport spærres af en adgangsmodel på 11 tabeller, der tjekkes på serveren.","summary":"Byggede betalings- og rettighedslaget — Stripe med Apple og Google in-app purchase — directory, søgning og eksport spærret af en adgangsmodel.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Penge er den del af en platform, ingen har lov til at tage let på. Et abonnement skal overleve, at et kort udløber, en refusion, et planskifte, en webhook, der ankommer to gange, og en webhook, der ankommer i forkert rækkefølge. I det øjeblik betaling kan ske i tre butikker — et kort på nettet, Apple i den ene app store, Google i den anden — findes der tre forskellige udlægninger af, hvad nogen har købt, og produktet har stadig brug for ét svar på ét spørgsmål: hvad må denne person lige nu?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e At tage imod penge og at give adgang skulle være to systemer frem for ét, så det at tilføje en butik ikke betød at omskrive hver eneste gate i produktet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Stripe håndterer kort og abonnementer gennem 18 Go‑filer, og Apple- og Google‑in‑app‑køb kommer ind gennem deres egen kvitteringsverifikation. Alle tre løber sammen i et payments‑skema på 9 tabeller og 34 funktioner — og stopper så dér. Det, produktet faktisk spørger om, er et separat access‑skema på 11 tabeller og 34 funktioner, som svarer på \u0026ldquo;må denne konto det her?\u0026rdquo; uden at vide eller bryde sig om, hvilken butik der har betalt for det. Det svar styrer adgangen til virksomhedskataloget, søgningen og dataeksporten, og det genkontrolleres på serveren ved hver request, for en skjult knap er en høflighed og ikke en kontrol.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e At tilføje en butik rører nu ved payments‑siden og lader alle gates være, og et supportspørgsmål om nogens adgang har én tabel at kigge i frem for tre. Omkostningen er to skemaer, hvor et mindre produkt ville nøjes med ét, plus et rettighedsopslag på requests, der ellers ville have været gratis.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Backend‑udvikling","Databaser","PostgreSQL","Produkt \u0026 krav","Sikkerhed","Backend- \u0026 API‑udvikling","Databasedesign \u0026 datamodellering","Produktstrategi \u0026 kravspecifikation","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/moved-slow-work-onto-a-river-job-queue-97/","url":"https://platform.engineer.company/da/portfolio/moved-slow-work-onto-a-river-job-queue-97/","title":"Har flyttet langsomt arbejde væk fra request‑stien og over på en River‑jobkø — 15 worker‑moduler, 8 planlagte opgaver og 20 pg_cron‑jobs — så et request vender tilbage, mens arbejdet bag det kører videre.","summary":"Flyttede langsomt arbejde væk fra request-stien over på en River-jobkø: 15 worker-moduler, 8 planlagte opgaver og 20 pg_cron-vedligeholdelsesjobs.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Noget arbejde har ingen gang på jorden, mens en bruger venter. At sende mail, at genopbygge et søgeindeks, at generere et dokument, at genberegne placeringer — gør noget af det inde i requesten, og brugeren kigger på en spinner for noget, de aldrig bad om at se. Gør det i stedet i en goroutine, og det forsvinder i det øjeblik processen genstarter, hvilket den gør, midt i en deploy, uden spor af, at det nogensinde skulle være sket.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Baggrundsarbejde havde brug for et holdbart sted at bo: en kø, der overlever en genstart, prøver en fejl igen og kan kigges efter, når noget ikke er sket.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Valget faldt på River, i høj grad fordi den holder sin kø i PostgreSQL — databasen er i forvejen det autoritative register, så et job og de rækker, det rører, committer eller ruller tilbage sammen, og der er ikke et stykke infrastruktur nummer to at køre og ræsonnere om. Bag den ligger 15 worker‑moduler og 8 planlagte opgaver. Under det håndterer 20 pg_cron‑jobs den vedligeholdelse, databasen er bedre placeret til at gøre selv: at beskære partitioner, at rotere salts, at opfriske aggregater. Alt, der var langsomt nok til at blive bemærket, blev flyttet væk fra request‑stien og over på en af de to.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Requests svarer hurtigt, og det langsomme arbejde bliver stadig færdigt, med retries og en synlig historik, når det ikke gør. At holde køen i Postgres frem for i en dedikeret broker er en bevidst begrænsning: det skalerer ikke i det uendelige, og ved en vis mængde bliver det det forkerte svar. For en platform, hvis flaskehals alligevel er databasen, var én bevægelig del mindre mere værd end luft, der ikke ville blive brugt.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Backend‑udvikling","Databaser","Drift \u0026 backup","Performanceoptimering","PostgreSQL","Backend- \u0026 API‑udvikling","Databasedesign \u0026 datamodellering","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/built-first-party-error-monitoring-and-tracing-98/","url":"https://platform.engineer.company/da/portfolio/built-first-party-error-monitoring-and-tracing-98/","title":"Har bygget egen error monitoring og OpenTelemetry‑tracing frem for at købe dem — sanitering af payloads, detektion af spikes og regressioner, symbolication og et syntetisk heartbeat — bag 11 operatørvisninger.","summary":"Byggede egen error monitoring og OpenTelemetry-tracing — sanitering, detektion af spikes, symbolication og et heartbeat — bag 11 operatørvisninger.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Forskellen på en platform, der er oppe, og en platform, der virker, er, om nogen ville vide det. En fejl, en bruger rammer klokken elleve om aftenen, på en side ingen tester, er usynlig, medmindre noget går ud og samler den op. Det sædvanlige svar er at købe en hosted error tracker, og det er et godt svar — og det betyder også, at platformens egne fejl, stack traces og brugerkontekst rejser af sted til en tredjepart.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Fejl og traces skulle samles, grupperes og gøres til noget, man kunne handle på, uden at platformens indre forlod platformen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Der blev bygget to stykker. OpenTelemetry står for tracing over OTLP, så en langsom request kan følges på tværs af frontenden, API\u0026rsquo;et og databasen frem for at blive gættet på. Ved siden af ligger en hjemmebygget fejl‑pipeline — en errmon‑service og en ingest‑service — der renser payloads før lagring, grupperer fejl i tilbagevendende problemer frem for en flad liste, opdager spikes og regressioner med en cooldown, så én dårlig deploy ikke kalder nogen ud fyrre gange, oversætter minificerede frontend‑stack traces tilbage til læsbar kode og kører en syntetisk heartbeat for at bevise, at selve pipelinen er i live. Det hele lander i databasen som error events, error groups, en inbox, API‑latens og stack‑samples, og det kommer frem gennem 11 operatørvisninger, heriblandt én til service level objectives.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Fejl bliver til en kø, nogen kan arbejde sig igennem, og en regression melder sig selv i stedet for at blive opdaget af en bruger. At bygge frem for at købe kostede reel tid og betyder, at det er én ting mere at vedligeholde — en købt tracker ville have kørt samme eftermiddag. Det, det købte, var, at intet følsomt forlader platformen, og at alarmreglerne passer til netop denne platform frem for til en generisk.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Backend‑udvikling","DevOps","Drift \u0026 backup","Infrastruktur","Monitorering \u0026 observability","Sikkerhed","Backend- \u0026 API‑udvikling","DevOps \u0026 CI/CD‑automatisering","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/kept-the-schema-honest-across-1022-migrations-99/","url":"https://platform.engineer.company/da/portfolio/kept-the-schema-honest-across-1022-migrations-99/","title":"Har holdt skemaet ærligt på tværs af 1.022 migrationer med en CI‑gate, der bygger databasen begge veje — en frisk installation og en installation plus hver eneste migration — og fejler, når de to ikke stemmer overens.","summary":"Holdt skemaet ærligt på tværs af 1.022 migrationer med en CI-gate, der bygger databasen begge veje og fejler, når de to ikke stemmer overens.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Et skema er beskrevet to gange i de fleste projekter: én gang af den indledende opsætning, der bygger det fra bunden, og én gang af de ophobede migrationer, der har fået det til at vokse. Begge er ment at producere den samme database. Intet tjekker, at de gør det, så de driver fra hinanden — og afdriften er usynlig, indtil et friskt miljø opfører sig anderledes end produktion, som regel på det værst tænkelige tidspunkt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e De to beskrivelser skulle beviseligt være identiske, automatisk, frem for periodisk at blive troet at være det.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Databasen er versioneret som 1.022 migrationer, nummereret fra 036 til 1102, og rækkefølgedisciplinen omkring dem er kedelig og ikke til forhandling. Det, der får den til at holde, er et CI‑job, der bygger databasen to gange ved hver ændring: én gang fra det friske initielle skema, én gang fra det initielle skema plus hver eneste migration afspillet i rækkefølge — og så sammenligner de to. Ikke bare strukturen, som er den nemme halvdel, men også de seedede data, for en migration, der backfiller en opslagstabel forkert, er præcis lige så skadelig som en, der glemmer en kolonne, og kun den ene af dem dukker op i en skema‑diff. Enhver uenighed får buildet til at fejle med forskellen printet ud.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Et friskt miljø og et langtlevende er den samme database, og det bliver tjekket frem for antaget. Omkostningen lander hos den, der skriver en migration: den skal virke afspillet, og den skal virke fra koldt, hvilket er mere tanke, end et hurtigt ALTER som regel får. Det er netop pointen — alternativet er at finde ud af det under en gendannelse, hvor svaret betyder noget, og der ikke er tid til at regne det ud.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Data governance","Databaser","DevOps","Drift \u0026 backup","Migrering \u0026 modernisering","PostgreSQL","Test \u0026 QA","Databaseadministration (DBA)","Databasemigrering \u0026 modernisering","DevOps \u0026 CI/CD‑automatisering"]},{"id":"https://platform.engineer.company/da/portfolio/built-fail-closed-abuse-controls-and-rate-limiting-100/","url":"https://platform.engineer.company/da/portfolio/built-fail-closed-abuse-controls-and-rate-limiting-100/","title":"Har bygget fail‑closed misbrugskontroller — 22 Redis‑baserede rate limiters, Cloudflare Turnstile, idempotens på requests og en origin‑lås — så platformen afviser bots og floods i stedet for at stole på sine kaldere.","summary":"Byggede fail-closed misbrugskontroller: 22 Redis-baserede rate limiters, Cloudflare Turnstile, idempotens på requests og en origin-lås ved indgangen.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Et offentligt katalog over virksomheder og fagfolk er et mål fra den dag, det går i luften. Scrapere vil have dataene, spamkonti vil have rækkevidden, og et endpoint, der koster platformen rigtige penge at levere — søgning, eksport, alt der rører en ekstern API — er værd at misbruge alene, fordi det er gratis at kalde. Intet af det er ondskab rettet mod netop denne platform; det er baggrundsvejr på det åbne internet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e De dyre stier og dem, der inviterer til misbrug, havde brug for grænser, der holder under pres — også presset fra, at limiterens egen afhængighed ikke er tilgængelig.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Der er 22 rate limitere, hver bygget til den sti, den beskytter, frem for ét globalt loft, for et loginforsøg, en søgning og en bulk‑eksport bliver misbrug ved vildt forskellige hastigheder. Tilstanden bor i Redis, så en grænse deles på tværs af instanser i stedet for at være per proces og trivielt at slippe uden om. Den vigtige beslutning er, hvad der sker, når Redis ikke er der: limiterne er fail‑closed. Trafik afvises frem for at blive vinket igennem, hvilket er det mindre bekvemme svar og det eneste forsvarlige. Omkring dem sidder Cloudflare Turnstile på de stier, der er værd at udfordre, 351 linjers idempotency‑middleware, så en genforsøgt skrivning ikke bliver til to, en origin‑lås, der afviser requests, som ikke kommer ind ad hoveddøren, og en skræddersyet challenge på selve kataloget.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Misbrug bliver dyrt for den, der misbruger, og billigt for platformen, og et udfald i limiterens eget lager degraderer til afvisning frem for til en åben dør. At være fail‑closed betyder ganske vist, at et Redis‑problem bliver et problem, brugeren kan se — accepteret med vilje, fordi alternativet er, at et Redis‑problem bliver et regningsproblem.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Backend‑udvikling","Drift \u0026 backup","Netværk \u0026 VPN","Performanceoptimering","Sikkerhed","Backend- \u0026 API‑udvikling","Sikkerhed \u0026 adgangsstyring","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/built-company-ownership-claims-end-to-end-103/","url":"https://platform.engineer.company/da/portfolio/built-company-ownership-claims-end-to-end-103/","title":"Har bygget ejerskabskrav på virksomheder fra ende til anden — en bruger gør krav på en virksomhed, en administrator afgør sagen, og en godkendelse omskriver den autorisationsgraf, der afgør, hvem der må redigere hvad.","summary":"Byggede ejerskabskrav på virksomheder fra ende til anden: en bruger gør krav, en administrator afgør, og godkendelsen omskriver autorisationsgrafen.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Et katalog seedet fra offentlige kilder har et strukturelt problem: virksomhederne i det har ikke selv sat sig der. Før eller siden dukker nogen fra en af dem op og vil rette sin egen post — og der er ingen relation mellem den person og den post, kun en påstand om, at der er en. Giver man det for villigt, redigerer en konkurrent din side. Giver man det for langsomt, bliver kataloget ved med at være forkert.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Der skulle være en vej fra \u0026ldquo;det her er min virksomhed\u0026rdquo; til reel myndighed over posten, med en menneskelig beslutning i midten og et spor bagefter.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Ejerskabskrav blev bygget fra ende til anden, over 108 commits og begge applikationer. En bruger indsender et krav med dokumentation; det lander i en kø i back‑officen; en administrator gennemgår det og godkender eller afviser det med en begrundelse, der går tilbage til den, der rejste kravet. Det interessante er, hvad en godkendelse gør — det er ikke et flag på en række. Godkendelsen omskriver autorisationsgrafen, så kontoen får en rigtig relation til organisationen, og det er den samme relation, som hvert eneste rettighedstjek i platformen allerede slår op i. Gaten er afgørelsen, ikke kodestien, og ingen funktion har været nødt til at lære om ejerskabskrav for at respektere den.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En virksomhed kan overtage og rette sin egen post, uden at nogen redigerer databasen i hånden, og hver tildeling af myndighed har en navngiven godkender og en begrundelse hæftet på. Den menneskelige gennemgang er flaskehalsen med vilje; et automatisk tjek på et domænenavn ville være hurtigere og ville tage fejl i præcis de tilfælde, der betyder mest.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Backend‑udvikling","Frontend‑udvikling","Full stack‑udvikling","Produkt \u0026 krav","Sikkerhed","Full stack‑produktudvikling","Produktstrategi \u0026 kravspecifikation","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/established-a-continuous-security-programme-104/","url":"https://platform.engineer.company/da/portfolio/established-a-continuous-security-programme-104/","title":"Har etableret et løbende sikkerhedsprogram — code scanning, DAST, afhængigheds- og sårbarhedstjek, SBOM‑generering, secret scanning og SHA‑pinnede actions — sammen med 21 skrevne sikkerhedsaudits.","summary":"Etablerede et løbende sikkerhedsprogram — code scanning, DAST, afhængighedstjek, SBOM, secret scanning og pinnede actions — plus 21 audits.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Sikkerhedsarbejdet inde i applikationen — Content‑Security‑Policy\u0026rsquo;en, least‑privilege‑databaserollerne, gentjekket af rettigheder — beskytter platformen ved runtime. Intet af det siger noget om det, der bliver sendt afsted: om en dependency samlede en kendt sårbarhed op i tirsdags, om en adgangsoplysning blev committet og rullet tilbage, eller om en third‑party‑action pinnet til et tag stille og roligt er blevet til et andet stykke kode.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Supply chain- og kodesikkerhed skulle være løbende og automatiseret, så tilstanden af det var et build‑resultat frem for en holdning.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Statisk analyse kører gennem CodeQL, dynamisk test mod en kørende instans gennem ZAP, og Go‑dependencies tjekkes med govulncheck. En SBOM genereres ved hvert build med Anchore og Syft, så det, der blev sendt afsted, er kendt frem for rekonstrueret bagefter. Gitleaks scanner historikken for adgangsoplysninger. Hver third‑party GitHub Action er pinnet til en commit‑hash frem for et tag, hvilket er den uglamourøse kontrol, der forhindrer, at et tag bliver flyttet under dig. Dependabot holder øje med 6 økosystemer. Ved siden af automatiseringen ligger 21 skrevne sikkerhedsaudits, heriblandt en threat model og en vurdering op mod OWASP‑testguiden — for scannere finder de klasser af problemer, nogen allerede har beskrevet, og en threat model er der, hvor dem, der er specifikke for netop denne platform, får et navn.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En sårbarhed, der bliver offentliggjort upstream, dukker op som et fejlende build frem for som en nyhed. Mængden af fund er den reelle omkostning — en scanner, der rapporterer alt, træner folk i at ignorere den, og at holde signalet brugbart kræver løbende triage frem for en engangsopsætning.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Drift \u0026 backup","Sikkerhed","DevOps \u0026 CI/CD‑automatisering","Sikkerhed \u0026 adgangsstyring","Teknisk dokumentation"]},{"id":"https://platform.engineer.company/da/portfolio/built-the-companys-infrastructure-as-code-108/","url":"https://platform.engineer.company/da/portfolio/built-the-companys-infrastructure-as-code-108/","title":"Har bygget virksomhedens egen infrastruktur som 19 Ansible‑playbooks og 34 roller fordelt på 12.065 linjer YAML, der konvergerer en levende vært til en erklæret tilstand med hvert play idempotent.","summary":"Miljøet kan reproduceres fra repositoryet, og de dele af det, der kun nogensinde var sande fordi nogen huskede dem, er nu assertions der fejler en kørsel.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Engineer ApS driver sit eget miljø — en webtilstedeværelse, en git‑forge, en database, sikkerhedskopier, mailtransport og DNS — og der var ingen at overdrage driften til. Et enmandsfirma har de samme fejltilstande som et stort og ingen af redundansen, hvilket gør det sædvanlige svar, en person der husker hvordan værten blev sat op, til den mindst tilgængelige mulighed der findes.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Hele miljøet skulle beskrives i et repository frem for i et hoved, og beskrives i en form der konvergerer en rigtig vært frem for at dokumentere en.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Det der voksede ud af det er 19 playbooks og 34 roller fordelt på 12.065 linjer YAML. Formen betyder mere end størrelsen. Sammensætning er data frem for flag: en vært ligger i en tier‑gruppe, hvis variabler erklærer hvilke roller den kører, så provisionering uden argumenter konvergerer hver vært til dens erklærede tilstand. Idempotens er en kontrakt frem for en ambition — en konvergeret vært rapporterer nul ændringer, og et play der ikke kan sige det, er ikke færdigt. Fire kommandonavnerum holder løfterne adskilt: en gate der ikke rører nogen vært, rapporter der læser en og aldrig ændrer den, provisionering der ændrer en vært så den matcher repositoryet, og verifikation der ændrer en vært med vilje og returnerer en dom.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Miljøet kan reproduceres fra repositoryet, og de dele af det, der kun nogensinde var sande fordi nogen huskede dem, er nu assertions der fejler en kørsel. Prisen er reel: hver ændring er langsommere at lave end at redigere en fil på serveren ville være, og en konvergering der halvt gennemføres er værre end en der nægter, hvilket er grunden til at et preflight‑play senere blev sat foran den. Den afvejning blev truffet bevidst, og den har holdt.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Platformarkitektur","Systemadministration","Cloud‑infrastruktur \u0026 migrering","DevOps \u0026 CI/CD‑automatisering","Infrastructure as Code"]},{"id":"https://platform.engineer.company/da/portfolio/ran-the-whole-company-on-one-512mb-host-109/","url":"https://platform.engineer.company/da/portfolio/ran-the-whole-company-on-one-512mb-host-109/","title":"Har kørt hele virksomheden på én vært med 512 MB og én kerne — en git‑forge, en webserver til syv domæner, Tor, to servere til alternative protokoller, backup og udelukkelse af indtrængen — ved at behandle 464 MB brugbar hukommelse som den bindende arkitektoniske begrænsning.","summary":"Hele virksomheden kører på en maskine der koster mindre om måneden end en frokost, og designet er bedre af disciplinen frem for blot billigere.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Virksomhedens produktionsvært er en cloud‑instans med én kerne, 512 MB hukommelse og 10 GB disk, hvoraf omkring 464 MB er anvendelige. Alt hvad forretningen kører offentligt ligger på den: webserveren der terminerer TLS for syv domæner, git‑forgen, en Tor‑onion‑tjeneste, en Gemini‑server, en Gopher‑server, krypterede sikkerhedskopier og indtrængningsblokering. Den sædvanlige reaktion på den liste er at købe en større maskine.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Begrænsningen skulle behandles som et arkitektonisk input frem for et problem man bruger penge på, for det ærlige spørgsmål var ikke om en større maskine ville virke, men om designet havde brug for en.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Hukommelse blev det argument der afgjorde beslutninger. Der er ingen overvågningsagent, ingen metrikpipeline og intet dashboard — rapportering er et pull, syv kommandoer der læser værten og gengiver Markdown, ændrer intet og kun kører når man spørger. Supervisoren er systemd frem for endnu en proceshåndtering lagt oven på den, og containerplanet er Quadlet‑units under den samme supervisor frem for en dæmon med sin egen. Webpanel‑platforme blev udelukket på designstadiet af samme grund. Da spørgsmålet kom op om værten kunne bære en onion‑tjeneste, kom svaret fra en dags målte stikprøver frem for fra en holdning: tilgængelig hukommelse faldt aldrig under omkring 310 MB af 464, swap lå på 2,6 procent og processoren var 99,7 procent inaktiv.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Hele virksomheden kører på en maskine der koster mindre om måneden end en frokost, og designet er bedre af disciplinen frem for blot billigere. Det den kostede er råderum til noget som helst skødesløst — post ligger bevidst slet ikke på denne maskine — den er skrevet som et installationsstillads, der venter på sin egen vært, fordi en mailserver kræver råderum, denne maskine allerede har brugt, og det er skrevet ned frem for opdaget senere.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Løsningsarkitektur","Performanceoptimering","Platformarkitektur","Systemadministration","Cloud‑infrastruktur \u0026 migrering","Infrastructure as Code","Platform- \u0026 løsningsarkitektur"]},{"id":"https://platform.engineer.company/da/portfolio/found-three-ssh-brute-force-protections-that-never-worked-110/","url":"https://platform.engineer.company/da/portfolio/found-three-ssh-brute-force-protections-that-never-worked-110/","title":"Har fundet og lukket tre SSH‑beskyttelser mod brute force, der aldrig havde virket: et bandlysningsfængsel, der holdt øje med port 22, mens dæmonen lyttede på 1986, en ratebegrænsning skygget af en bredere regel over den, og en bandlysningshandling, hvis binærfil aldrig blev fundet, så ingen bandlysning nogensinde var trådt i kraft.","summary":"Tre beskyttelser, der aldrig én eneste gang havde udløst, gør det nu, og den fejlklasse de tilhører — en kontrol hvis fejltilstand er, at den bliver ved med at…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En hærdningsgennemgang af produktionsværten i august 2026 stillede et spørgsmål, der normalt får et selvsikkert svar: virker SSH‑beskyttelserne mod brute force. Alle tre var konfigureret, alle tre optrådte i hver rapport nogen kiggede i, og alle tre havde været uvirksomme siden den dag værten blev bygget.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Kontrollerne skulle efterprøves mod hvad kernen rent faktisk gør ved en pakke, frem for mod de konfigurationsfiler der beskriver hvad der burde ske med en.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e At læse konfigurationen ville have bekræftet det forkerte svar tre gange, så gennemgangen læste det kørende system i stedet. Blokeringsfængslet holdt øje med port 22, mens dæmonen var flyttet til 1986 under den indledende hærdning — hver blokering den skrev navngav en port, som intet lyttede på. Firewallens hastighedsbegrænsning var værre på en mere subtil måde: reglen fandtes, og den lå under en bredere regel, der matchede først. Firewallens brugerregler evalueres oppefra og ned, og det første match vinder, så en bred tilladelse over en hastighedsbegrænsning gør begrænsningen til død kode, der stadig står i hver statusoversigt. Den tredje var den mest tavse af dem: blokeringshandlingen kalder ud til et pakkefilterprogram, som pakkesystemet kun anbefaler frem for kræver, så på en vært uden det starter fængslet, tæller og beslutter, og fejler så i det ene øjeblik det forsøger at blokere. Alle tre rettelser var små. Det der kom ud af det var ikke rettelsen, men to regler der nu styrer repositoryet: en sikkerhedskontrol får en assertion frem for en kommentar, og en firewall verificeres på regelplacering frem for på regelforekomst.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Tre beskyttelser, der aldrig én eneste gang havde udløst, gør det nu, og den fejlklasse de tilhører — en kontrol hvis fejltilstand er, at den bliver ved med at rapportere sundt — er den klasse platformens tjek nu er bygget til at fange. Alle tre havde været uvirksomme fra bootstrap. Alle tre stod som sunde alle de steder nogen kiggede, hvilket er hele grunden til at de varede ved.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Infrastruktur","Linux \u0026 servere","Monitorering \u0026 observability","Netværk \u0026 VPN","Sikkerhed","Systemadministration","Netværk \u0026 VPN‑opsætning","Sikkerhed \u0026 adgangsstyring","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/hardened-ssh-with-three-stage-validation-111/","url":"https://platform.engineer.company/da/portfolio/hardened-ssh-with-three-stage-validation-111/","title":"Har hærdet SSH til 24 hævdede direktiver med validering i tre trin — kandidatfilen, den samlede konfiguration og dernæst dæmonens egen tilbagelæsning — efter at tilbagelæsningen fangede den kørende server i stilhed at tilsidesætte to af de fireogtyve.","summary":"SSH-positionen er nu dæmonens svar frem for repositoryets påstand, og forskellen er ikke teoretisk — den var allerede to indstillinger bred, da tjekket blev…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e SSH er den eneste interaktive vej ind på virksomhedens vært, og dens konfiguration skrives af en automatiseringsrolle, der havde kørt rent i måneder. En adgangsrapport i september 2026 læste dæmonens egne resolverede indstillinger og fandt to af dem i uoverensstemmelse med det, rollen havde skrevet ved hver eneste konvergering.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Hærdningen skulle blive noget dæmonen bekræfter frem for noget repositoryet påstår, for afstanden mellem de to havde allerede stået åben i måneder, uden at nogen bemærkede det.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Årsagen var konfigurationsrækkefølge. Styresystemet leverer sine egne standarder ukommenterede, over det sted hvor en drop‑in‑fil lander, og for de to omtalte indstillinger vinder den første forekomst. Rettelsen var et filnavnspræfiks, der sorterer foran leverandørens, hvilket er en ændring på ét tegn og præcis den slags, der forbliver i stykker, fordi ingen tænker på at kigge efter. Det der blev bygget omkring den betyder mere: tre valideringstrin ved hver konvergering. Kandidatfilen syntakstjekkes før den installeres, så en ugyldig konfiguration aldrig når værten. Den samlede konfiguration tjekkes efter installationen. Derefter læses dæmonens eget resolverede output tilbage, og 24 direktiver efterprøves mod det, så en indstilling der er skrevet men overskrevet fejler kørslen. To direktiver blev bevidst udeladt, begge fordi dæmonen ikke længere implementerer dem, og at skrive dem ville kun se grundigt ud.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e SSH‑positionen er nu dæmonens svar frem for repositoryets påstand, og forskellen er ikke teoretisk — den var allerede to indstillinger bred, da tjekket blev skrevet. En konvergering der ikke kan se om en kontrol tog effekt, har ikke verificeret den, og at søge efter et direktiv beviser kun at det blev skrevet.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Infrastruktur","Linux \u0026 servere","Sikkerhed","Systemadministration","Test \u0026 QA","Infrastructure as Code","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/proved-the-intrusion-banning-path-on-every-converge-112/","url":"https://platform.engineer.company/da/portfolio/proved-the-intrusion-banning-path-on-every-converge-112/","title":"Har bevist hele bandlysningsstien ved hver hærdningskørsel ved at bandlyse en reserveret testadresse, læse den resulterende kerneregel og ophæve bandlysningen i en garanteret oprydningsblok, så et fængsel, der holder op med at virke, får kørslen til at fejle i stedet for at melde sig rask.","summary":"En blokeringsvej der holder op med at virke, fejler nu en konvergering i stedet for at blive ved med at rapportere sundt, hvilket var den eneste egenskab der…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Indtrængningsblokeringstjenesten var allerede blevet grebet i at blokere en port, som SSH‑dæmonen ikke lyttede på. At rette porten lukkede det tilfælde. Det gjorde intet ved grunden til at fejlen overlevede så længe, nemlig at en blokeringsvej ikke har nogen synlig fejl: tjenesten kører, fængslet står som aktivt, og intet noget sted siger, om en blokering nogensinde når kernen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Blokeringsvejen skulle afprøves ved hver konvergering, mod det levende regelsæt, frem for udledes af at tjenesten var oppe.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Konvergeringen blokerer nu en adresse fra den blok, standarderne reserverer til dokumentation, læser den resulterende regel tilbage i kernens pakkefilter og ophæver blokeringen i en oprydningsblok, der kører uanset om tjekket bestod eller fejlede. Det reserverede område er det bærende valg — testadressen tilhører ingen, så en efterladt blokering, der overlever en fejlet kørsel, ikke kan lukke et rigtigt netværk ude. Tilbagelæsningen fra kernen er den anden halvdel: tjenestens egen statusudskrift ville rapportere succes for en blokering, der ikke frembragte nogen regel, hvilket er præcis den fejl der testes for. Ved siden af den er blokeringsbackenden fastlåst frem for overladt til tjenestens egen detektion, og logbackenden er sat til detektion, fordi styresystemet ikke leverer nogen traditionel autentificeringslog, og det forkerte valg fejler tavst ved at holde øje med en fil, der aldrig dukker op.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En blokeringsvej der holder op med at virke, fejler nu en konvergering i stedet for at blive ved med at rapportere sundt, hvilket var den eneste egenskab der betød noget. Det koster nogle få sekunder ved hver kørsel, og det skriver og trækker en firewallregel tilbage mod produktion hver gang, hvilket er et reelt indgreb i et levende system og blev accepteret på den baggrund, at alternativet allerede var påvist at være værre.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Sikkerhed","Test \u0026 QA","Sikkerhed \u0026 adgangsstyring","Site reliability \u0026 monitorering","Systemadministration"]},{"id":"https://platform.engineer.company/da/portfolio/verified-firewall-rules-by-position-113/","url":"https://platform.engineer.company/da/portfolio/verified-firewall-rules-by-position-113/","title":"Har verificeret firewallregler efter position frem for tilstedeværelse ved at læse den nummererede regelliste og den levende pakkefilterkæde, for en regel, der findes, er ikke en regel, nogen pakke når frem til.","summary":"Firewallpositionen tjekkes nu på den måde en pakke oplever den. Det mest nyttige udfald var ikke tjekket, men hvad det afslørede om den rapportering der gik…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En firewallstatusoversigt viser et sæt regler. Et pakkefilter evaluerer en ordnet liste og standser ved det første match. Det er forskellige ting, og forskellen er usynlig i hvert eneste værktøj der udskriver et sammendrag — hvilket er sådan værten kørte et år med en hastighedsbegrænsning, som ingen pakke nogensinde nåede, liggende under en bredere regel der matchede først.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Firewallverifikation skulle læse placering frem for medlemskab, for forekomst var allerede påvist ikke at bevise noget.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Tjekkene læser nu den nummererede regeloversigt og den levende filterkæde og efterprøver mod rækkefølge. Der findes præcis én regel per port og altid med en protokol, for en regel uden en udvider tavst overfladen. SSH‑porten bærer en hastighedsbegrænsning og aldrig en tilladelse ved siden af, eftersom en tilladelse over en begrænsning er præcis den overskygning der blev fundet. Den erklærede offentlige overflade er en kort liste på fem porte, efterprøvet som en helhed frem for tjekket enkeltvis, så en port der dukker op uden at være erklæret, fejler frem for at blive bemærket. Sikkerhedsrapporten gengiver reglerne i matchrækkefølge med en advarsel øverst i afsnittet om hvordan den læses, for den næste der åbner den fil, vil ellers læse et sæt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Firewallpositionen tjekkes nu på den måde en pakke oplever den. Det mest nyttige udfald var ikke tjekket, men hvad det afslørede om den rapportering der gik forud: hvert eneste involveret værktøj havde udskrevet hastighedsbegrænsningen i et år, korrekt, og ingen af dem var blevet stillet det ene spørgsmål der betød noget.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Infrastruktur","Linux \u0026 servere","Netværk \u0026 VPN","Sikkerhed","Test \u0026 QA","Netværk \u0026 VPN‑opsætning","Sikkerhed \u0026 adgangsstyring","Systemadministration"]},{"id":"https://platform.engineer.company/da/portfolio/built-encrypted-backups-with-a-monthly-restore-drill-114/","url":"https://platform.engineer.company/da/portfolio/built-encrypted-backups-with-a-monthly-restore-drill-114/","title":"Har bygget krypteret backup uden for værten på restic med opbevaringsbeskæring, et integritetstjek og en månedlig automatiseret gendannelsesøvelse, og har derefter auditeret gendannelsespositionen og skrevet hullerne ned frem for at lade dem blive fundet under en hændelse.","summary":"Virksomheden kan miste værten og få sine data tilbage, og den sætning hviler på en gendannelse der kørte sidste måned frem for på en sikkerhedskopi der kørte i…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Alt hvad virksomheden rummer — git‑repositorierne, databasen, sitet — lå på én cloud‑instans, hvis udbyder‑snapshot var hele gendannelsespositionen. Et udbyder‑snapshot er en fin ting at have og en dårlig ting at læne sig på: det ligger i den samme konto som den maskine det beskytter, det er ikke krypteret af nogen her, og ingen havde nogensinde gendannet fra et.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Sikkerhedskopier skulle være krypterede, uden for værten, beskåret efter en opbevaringspolitik, og — den del der som regel springes over — faktisk gendannet fra, efter en plan, uden at en person skulle huske at gøre det.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Sikkerhedskopieringen kører på en systemd‑timer: et databasedump hvor et sådant findes, derefter et krypteret dedupliceret snapshot til lager hos en anden udbyder over SFTP, derefter en beskæring efter opbevaringspolitik, derefter et integritetstjek. En dead man\u0026rsquo;s switch pinges kun ved succes, hvilket er den skelnen der gør den til en alarm frem for en log — en kørsel der fejler siger intet, og at sige intet er det der udløser alarmen. Særskilt kører en gendannelsesøvelse månedligt: den trækker en kendt fil ud af repositoryet og sammenligner den, så det der tjekkes er en gendannelse frem for en sikkerhedskopi. Adgangssætningen skrives til en fil, som unitsene læser, frem for at blive sendt gennem miljøet, for supervisoren behandler undvigetegn i miljøværdier, og en adgangssætning med en omvendt skråstreg ville tavst have været en anden end den, der skabte repositoryet. Bagefter blev gendannelsespositionen auditeret og skrevet op, og de tilbageværende huller blev navngivet i dokumentet frem for efterladt til at blive opdaget under en hændelse.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Virksomheden kan miste værten og få sine data tilbage, og den sætning hviler på en gendannelse der kørte sidste måned frem for på en sikkerhedskopi der kørte i nat. Auditens mest værdifulde output var listen over ting der stadig ikke er dækket, hvilket er den del en grøn sikkerhedskopirapport strukturelt er ude af stand til at fortælle nogen.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Dokumentation","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Sikkerhed","Backup \u0026 disaster recovery","Infrastructure as Code","Site reliability \u0026 monitorering","Systemadministration"]},{"id":"https://platform.engineer.company/da/portfolio/built-dead-mans-switch-monitoring-115/","url":"https://platform.engineer.company/da/portfolio/built-dead-mans-switch-monitoring-115/","title":"Har bygget dødmandsknap‑monitorering, der kun pinger, mens hukommelse og disk er sunde, så en svækket vært udløser en alarm ved at gå tavs — og fangede seks variabelnavne, der sagde \"free\", hvor tjekket korrekt målte \"available\", en størrelsesorden fra hinanden på en boks med 464 MB.","summary":"Værten har nu en alarm, hvis fejltilstand er at udløse, og den navngivning der ville have ødelagt den, er rettet.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En vært på 464 MB der kører en forge, en database og en webserver har to realistiske måder at dø på: den løber tør for hukommelse, eller den løber tør for diskplads. Ingen af dem melder sig selv. Begge er fuldt forudsigelige nogle timer i forvejen, hvis noget kigger, og intet kiggede.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Værten havde brug for en alarm der virker, når værten ikke gør — hvilket udelukker alt der skal sende en besked i selve fejløjeblikket.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Svaret er en dead man\u0026rsquo;s switch på en timer. Hvert kvarter med jitter måler et lille script tilgængelig hukommelse og fri diskplads og pinger kun en ekstern tjeneste, hvis begge ligger over deres tærskler. Tavshed er alarmen. En maskine der er løbet tør for hukommelse, har mistet sit netværk eller er holdt op med at starte, frembringer præcis det samme signal som en der er usund, hvilket er den rigtige opførsel og grunden til at denne form blev valgt frem for en agent der rapporterer en status. Målingen er tilgængelig hukommelse, ikke fri hukommelse, og den skelnen blev til den mest lærerige del af arbejdet: scriptet havde læst den rigtige kolonne hele tiden, mens seks variabelnavne omkring det sagde \u0026ldquo;fri\u0026rdquo;. På en sund Linux‑maskine er fri hukommelse tæt på nul, fordi kernen bruger ledig hukommelse til cache, så en læser der holdt de navne op mod en tærskel på ti procent, ville se tolv megabyte fri på en maskine med 464 MB, konkludere at tjekket var i stykker, og rette det med en ændring på ét tegn, der forvandler en fungerende alarm til en der bryder permanent og bliver slået fra inden for en uge.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Værten har nu en alarm, hvis fejltilstand er at udløse, og den navngivning der ville have ødelagt den, er rettet. Switchens reelle begrænsning står i dens egen dokumentation: den beviser at maskinen er sund, ikke at sitet leverer, og det er forskellige spørgsmål.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Monitorering \u0026 observability","Performanceoptimering","Infrastructure as Code","Site reliability \u0026 monitorering","Systemadministration"]},{"id":"https://platform.engineer.company/da/portfolio/made-ansible-check-mode-tell-the-truth-116/","url":"https://platform.engineer.company/da/portfolio/made-ansible-check-mode-tell-the-truth-116/","title":"Har fået check mode til at sige sandheden på hele platformen efter at have fundet seks sonder, der traf beslutning på en værdi, værten aldrig gav, fordi Ansibles command‑modul melder succes under --check, mens det springer kommandoen helt over.","summary":"En prøvekørsel måler nu enten noget eller siger at den ikke gjorde, og ingen af de to er den tredje mulighed den plejede at have.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En prøvekørsel mod produktion er ment som den sikre måde at finde ud af, hvad en ændring vil gøre. I september 2026 fejlede en prøvekørsel på en assertion, der ganske enkelt tog fejl om værten, og rådede operatøren til at sætte et flag, der ville have lempet en sikkerhedssandkasse. Prøvekørslen havde ikke læst værten. Den havde læst en værdi, værten aldrig gav, og draget en konklusion af den.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Hver sonde, hvis resultat fodrer en beslutning, skulle gøres ærlig under check‑tilstand, og de der ikke kunne, skulle sige det højt frem for at forblive tavse.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Årsagen er en egenskab ved værktøjet, som er dokumenteret og let at glemme: kommandomodulet udføres ikke under check‑tilstand, og det den registrerer er ikke et tomt resultat — det er en succes med tomt output. Enhver betingelse der læser den registrering, beslutter derfor på en værdi der aldrig blev målt, og den beslutter i den retning dens egen logik tilfældigvis peger. En gennemgang af hver registreret sonde fandt seks af dem, hver med sin egen løgn: en rapporterede at supervisoren accepterer hvert unit‑direktiv uden at have spurgt supervisoren, en anden rapporterede intet at gøre på en vært med firewallen slået fra. Hver af dem fik en af to former. En sonde der læser eksisterende tilstand, som kørslen ikke har rørt, markeres til at køre også under check‑tilstand. En sonde der ikke kan køre, springes over, og en besked navngiver hvad der specifikt ikke blev verificeret — for tavshed i en kørselslog læses præcis som en bestået.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En prøvekørsel måler nu enten noget eller siger at den ikke gjorde, og ingen af de to er den tredje mulighed den plejede at have. Den generelle regel kom i udviklingsvejledningen i samme ændring: check‑tilstand må ikke lyve, og en sonde der ikke kan se, er forpligtet til at melde sin blindhed frem for at udlede en dom af den.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Infrastructure as Code","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/added-a-preflight-play-for-secrets-117/","url":"https://platform.engineer.company/da/portfolio/added-a-preflight-play-for-secrets-117/","title":"Har tilføjet et preflight‑play, der kører den samme kode som konvergensen mod operatørens lokale hemmeligheder på omkring et sekund, efter at en halvt anvendt produktionskørsel døde på sin niende opgave med swap‑indstillingerne allerede skrevet til den levende vært.","summary":"En hemmelighed der ikke resolverer, koster nu en to-sekunders afvisning på en bærbar i stedet for en delvist gennemført ændring på en levende server.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Driftshemmeligheder var lige blevet delt op, så hver vært bærer sit eget sikkerhedskopi‑repository, sin egen adgangssætning og sin egen alarmswitch. Repository‑koden var korrekt. Operatørens krypterede boks holdt stadig den tidligere enkeltværtsværdi, og intet kunne se uoverensstemmelsen, for boksen er bevidst lokal for operatørens maskine og usynlig for repositoryets egen kvalitetsgate.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Den fejlklasse havde brug for et sted at fejle billigt, for den havde lige fejlet dyrt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Den første konvergering efter ændringen forbandt til den levende vært, kørte otte opgaver og nægtede på den niende — korrekt, men inde fra en kørsel der allerede havde skrevet swap‑indstillinger til produktion. En halvt gennemført konvergering er et værre svar end en afvisning, så et preflight‑play blev skrevet: det når slet ingen vært, kører lokalt, indsamler ingen facts og udfører de samme to opgavefiler, som den rigtige konvergering bruger, mod operatørens boks. Det svarer på omkring et sekund, og provisionering kører det først. Den bærende beslutning er, at det er den samme kode frem for en anden implementering af den samme regel, for et tjek der gentager en regel på et andet sprog, ender med at være uenig med den, og at være uenig tavst. At skrive det bragte en fælde frem, som det næsten selv skabte: facts sat under et play overlever det play, så at kæde preflight og konvergering sammen i én kørsel ville have givet den rigtige rolle en adgangssætning, som preflight allerede havde resolveret, og bestået tjekket på en boks der stadig var forkert. Det blev reproduceret før det blev afværget, i begge opgavefiler.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En hemmelighed der ikke resolverer, koster nu en to‑sekunders afvisning på en bærbar i stedet for en delvist gennemført ændring på en levende server. Preflight er bevidst ikke mærket til altid at køre og bevidst ikke serialiseret, og begge dele er noteret som beslutninger frem for standarder.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Drift \u0026 backup","Infrastruktur","Sikkerhed","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Infrastructure as Code","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/reconciled-a-dns-zone-declaratively-118/","url":"https://platform.engineer.company/da/portfolio/reconciled-a-dns-zone-declaratively-118/","title":"Har afstemt en DNS‑zone med 20 poster deklarativt mod Cloudflares API med separate indgange til audit og BIND‑eksport, og har slået CDN‑proxyen fra igen af hensyn til privatlivet efter at have bygget den.","summary":"Zonen er versioneret, sammenlignelig og eksporterbar, og den ene beslutning der gik imod den oplagte standard, er skrevet ned med sin begrundelse, så ingen…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Virksomhedens DNS bar omkring tyve poster fordelt på en webtilstedeværelse, et git‑underdomæne, to personlige omdirigeringer, mailrouting gennem en hosted postkasseudbyder, et transaktionsafsendelsesdomæne og autentificeringsposterne for begge. Det hele lå i en udbyders webkonsol, hvilket betyder at den aktuelle tilstand var, hvad den sidste person der klikkede havde efterladt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Zonen skulle blive til en erklæring i repositoryet, med en måde at sammenligne den erklæring med det, udbyderen faktisk leverer.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Zonen er skrevet som data — levende poster, applikationsposter, transportpolitikposter og en særskilt liste over poster der afventer fjernelse, hvilket er den ærlige måde at registrere en sletning der endnu ikke er sket. Ét play afstemmer den mod udbyderens API. To yderligere indgange ligger bag eksplicitte tilvalgsmærker: en audit der rapporterer forskellen uden at ændre noget, og en eksport der skriver zonen ud i det standardiserede zonefilformat, så den kan læses af noget der ikke er dette repository. Den mest interessante beslutning var en tilbagerulning. At sende de to webflader gennem udbyderens indholdsnetværk blev bygget, virkede, og blev derefter slået fra igen — for det koster den ene sætning, virksomhedens privatlivsside handler om. Med en proxy foran håndterer en anden virksomhed hver besøgendes adresse og hver URL de beder om, før vi gør, under deres politik frem for vores. For en forretning hvis kendetegnende påstand er, at ingen kigger med, er det en dårligere handel end de angreb den forsvarer imod.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Zonen er versioneret, sammenlignelig og eksporterbar, og den ene beslutning der gik imod den oplagte standard, er skrevet ned med sin begrundelse, så ingen genoptager sagen ved et uheld. Auditvejen er den del der bruges mest, for at kende forskellen er oftere det man vil have end at lukke den.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Automatisering \u0026 CI/CD","Infrastruktur","Netværk \u0026 VPN","Python","Sikkerhed","Infrastructure as Code","Netværk \u0026 VPN‑opsætning","Sikkerhed \u0026 adgangsstyring","Systemadministration"]},{"id":"https://platform.engineer.company/da/portfolio/cut-systemd-sandbox-exposure-across-every-unit-119/","url":"https://platform.engineer.company/da/portfolio/cut-systemd-sandbox-exposure-across-every-unit-119/","title":"Har skåret systemd‑sandkassens eksponering ned på hver eneste unit, platformen selv installerer — en dødmandsknap‑tjeneste fra 9,6 UNSAFE til 1,5, en internetvendt git‑forge fra 8,3 EXPOSED til 1,5 — og har tilføjet et parsertjek ved konvergens efter at have fundet et stavefejlsbehæftet direktiv ignoreret i stilhed i tre unit‑skabeloner.","summary":"Hver unit kører med de privilegier den har brug for og ikke dem den ikke har, tallene er målte frem for påståede, og den fejlklasse hvor en tastefejl bliver…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Tretten unit‑filer blev installeret af dette repository og kører på produktionsværten med de privilegier, supervisoren giver som standard, hvilket er de fleste af dem. En hærdningsscore satte dead man\u0026rsquo;s switch‑tjenesten til 9,6 og kaldte den usikker; den internetvendte git‑forge scorede 8,3 og eksponeret. Begge tal var korrekte, og ingen af dem havde foranlediget noget, for en score uden en tærskel knyttet til sig er et tal, folk lærer at springe over.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Hver unit havde brug for en sandkasse proportional med hvad den faktisk gør, og sandkasserne havde brug for et tjek, for fejltilstanden ved en forkert sandkasse er, at unitten starter og så går i stykker et sted, parseren ikke kan se.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Privilegiebegrænsninger kom ind i hver unit‑skabelon — et privilegieloft fastlåst ved unittens eget, et systemkaldsfilter, begrænsede adressefamilier og hukommelse der er skrivbar eller eksekverbar, men aldrig begge dele. De målte resultater: dead man\u0026rsquo;s switch gik fra 9,6 til 1,5, forgen fra 8,3 til 1,5, og begge sikkerhedskopi‑units fra 9,6 til 2,3 og 2,5. At skrive dem bragte noget bedre frem end scorerne. Ét direktiv havde været stavet forkert i tre unit‑skabeloner, så længe de roller havde eksisteret — et plausibelt udseende navn der ikke findes, hvilket supervisoren svarer på ved at logge at den ikke genkender nøglen og starte unitten alligevel. At søge efter et direktiv beviser at det blev skrevet; kun parseren beviser at det tog effekt. Så en verifikationsrolle kører nu hver unit gennem supervisorens egen verifikator under konvergeringen og inkluderes af hver rolle der installerer en unit. Hvad det stadig ikke kan se, står klart i de samme dokumenter: hvert direktiv der kan ødelægge disse units, ødelægger dem ved kørsel, ikke ved parsning, og en score kan ikke sige om et script stadig når sin sidste linje.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Hver unit kører med de privilegier den har brug for og ikke dem den ikke har, tallene er målte frem for påståede, og den fejlklasse hvor en tastefejl bliver til en tavst fraværende kontrol, fejler nu en kørsel. Målingens grænser er noteret ved siden af den, hvilket er den del der holder den næste læser fra at overfortolke et grønt bånd.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Containere (Docker/Kubernetes)","Infrastruktur","Linux \u0026 servere","Sikkerhed","Test \u0026 QA","Containerisering \u0026 orkestrering","Infrastructure as Code","Sikkerhed \u0026 adgangsstyring","Systemadministration"]},{"id":"https://platform.engineer.company/da/portfolio/built-seven-read-only-host-reporting-roles-120/","url":"https://platform.engineer.company/da/portfolio/built-seven-read-only-host-reporting-roles-120/","title":"Har bygget syv skrivebeskyttede rapporteringsroller, der gengiver en levende vært som Markdown — fakta, adgang, git, metrikker, trafik, sikkerhed og udbyderinventar — under en regel om, at intet tal printes, som kørslen ikke har målt.","summary":"Miljøet kan beskrives fra repositoryet på forlangende, og rapporterne har fundet virkelige ting: de to døde sikkerhedskontroller, en uerklæret lyttende port,…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Værten havde intet dashboard og fik ikke et, for en metrikstak passer ikke på 464 MB og ville ikke have været sin pris værd, hvis den gjorde. Det efterlod et ærligt hul: der var ingen måde at besvare spørgsmål som hvem har adgang, hvor meget er dette repository vokset, hvordan ser trafikken ud, eller er hærdningen faktisk håndhævet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e De spørgsmål havde brug for svar, der kunne frembringes på forlangende, ikke kostede værten noget mens ingen spurgte, og aldrig kunne ændre det de beskrev.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Syv skrivebeskyttede roller, hver der gengiver en levende vært til Markdown i repositoryet. Et faktabillede der dækker styresystem, hardware, lagring, netværk, tjenester, pakker, lyttende sockets og processorudnyttelse beregnet ud fra to stikprøver af kernens egen tæller. En adgangsrapport der dækker konti, sudo‑omfang, nøglefingeraftryk, forge‑brugere og databaseroller. En git‑rapport per repository. En metrikrapport over dagens indsamlede stikprøver. En trafikrapport bygget på webserverens privatlivsmaskerede log. En udbyderoversigt på tværs af fire udbyderflader — DNS, to cloud‑API\u0026rsquo;er og et API til dedikerede servere. Og en sikkerhedsrapport der besvarer, om hærdningen er håndhævet frem for konfigureret, og udskriver firewallreglerne i matchrækkefølge og det levende blokeringsregelsæt. Den styrende regel på tværs af alle syv er, at intet tal udskrives, som kørslen ikke målte — en rapport der fylder et hul med et plausibelt tal, er værre end en der lader det stå tomt, for det er kun det plausible der bliver troet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Miljøet kan beskrives fra repositoryet på forlangende, og rapporterne har fundet virkelige ting: de to døde sikkerhedskontroller, en uerklæret lyttende port, og den iagttagelse at trafikarkivet kun er så gammelt som sidste gang nogen huskede at høste det. Den sidste er skrevet op som en begrænsning med en konkret pris noteret — seksten dages historik for ét site, der roterede væk mellem høstninger og ikke findes nogen steder.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Dokumentation","Infrastruktur","Linux \u0026 servere","Monitorering \u0026 observability","Stakeholder \u0026 rapportering","Site reliability \u0026 monitorering","Systemadministration","Teknisk dokumentation"]},{"id":"https://platform.engineer.company/da/portfolio/deployed-the-companys-own-git-forge-121/","url":"https://platform.engineer.company/da/portfolio/deployed-the-companys-own-git-forge-121/","title":"Har udrullet virksomhedens egen git‑forge på Soft Serve, privat som udgangspunkt og uden webpanel, med SSH‑porten bundet til loopback bag en jump‑vært, og har gjort landingssiden foran den til et build‑artefakt af hovedsitet frem for en håndholdt kopi.","summary":"Virksomheden hoster sin egen kode på sin egen hardware, og siden foran den arver hvert tjek hovedsitet består frem for at drive væk fra det på måder, kun en…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Virksomhedens kildekode lå hos en tredjeparts hostingtjeneste, hvilket er et rimeligt sted til den og en dårlig pasform for en forretning, hvis argument over for kunderne er, at den ikke overlader deres data til mellemled. At køre en forge i stedet betyder at køre en forge: autentificering, adgangskontrol, lagring, sikkerhedskopier og et offentligt ansigt til den.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e En kanonisk git‑server skulle rejses på den eksisterende vært, med den mindst mulige angrebsflade og intet webadministrationspanel, og en landingsside foran den, der ikke rådner.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Forgen er en enkelt binær overvåget af styresystemet, installeret fra leverandørens pakkerepository, bevidst valgt for at være SSH‑først uden administrativ webgrænseflade — jo mindre grænseflade, jo mindre at forsvare. Den er privat som standard: anonym adgang afvises, nøglefri adgang afvises, og hvert erklæret repository er markeret privat frem for at læne sig på uklarhed. Dens SSH‑lytter binder sig kun til loopback‑grænsefladen på port 23231 og nås udefra gennem en jump host, så firewallens erklærede overflade ikke vokser. En protokolmultiplexer, der ville lægge HTTPS og git‑SSH på én offentlig port ved at inspicere de første bytes af en forbindelse, er skrevet og klar bag en hovedafbryder, og den afbryder er slukket: git for flere brugere over den offentlige port er ikke nødvendigt endnu, og en lytter ingen bruger er overflade. Landingssiden foran den var det mere interessante problem. Den havde været en håndholdt kopi af hovedsitets design i sit eget repository, og hver eneste forskel der nogensinde blev fundet mellem de to, viste sig at være et uheld frem for en beslutning: en typeskala der gengav ordmærket omkring ni procent for stort, en fonterklæring der slog to vægte sammen til én på enhver maskine med familien installeret, ornamenter skjult under en bestemt bredde så de var fraværende på hver telefon, en vægt brugt uden en font leveret til den, og intet hovedlandmærke eller overskrift på øverste niveau på nogen side. Fem ud af fem, og ikke én synlig på et skærmbillede. Kopien blev slettet; landingssiden bygges nu af hovedsitets egne skabeloner og leveres som et artefakt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Virksomheden hoster sin egen kode på sin egen hardware, og siden foran den arver hvert tjek hovedsitet består frem for at drive væk fra det på måder, kun en måling kan se. Afvigelse er stadig mulig og skal nu skrives ned.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Infrastruktur","Linux \u0026 servere","Systemadministration","Webudvikling","DevOps \u0026 CI/CD‑automatisering","Infrastructure as Code","Webstedsudvikling \u0026 CMS"]},{"id":"https://platform.engineer.company/da/portfolio/deployed-a-container-plane-on-podman-and-quadlet-122/","url":"https://platform.engineer.company/da/portfolio/deployed-a-container-plane-on-podman-and-quadlet-122/","title":"Har udrullet et containerplan på Podman og Quadlet under systemd frem for Docker, fordi Docker publicerer containerporte over værtens egne firewallregler — og har givet deploys en uprivilegeret bruger med én fast kommando i stedet for root.","summary":"Containere kører under den supervisor, der allerede var betroet, bag den firewall der allerede var erklæret, og en rutinemæssig release kræver ingen…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Platformen havde brug for et sted at køre applikationscontainere. Det oplagte valg var branchestandarden, og det oplagte valg var forkert for denne vært på en bestemt måde: den omskriver kernens pakkefilterregler og publicerer containerporte over de firewallregler, hærdningsrollen installerer, så en container tavst bliver tilgængelig fra internettet uanset hvad firewallen fik at vide.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Der skulle vælges et containerplan, som ikke går uden om firewallen, ikke tilføjer endnu en supervisor ved siden af den, der allerede var betroet, og ikke kræver at en person er root for at sende en release ud.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Planet er en dæmonløs containermotor, der driver units genereret af styresystemets egen supervisor. Der er ingen anden proceshåndtering: containere er tjenester, de starter som tjenester starter, og de beskrives i den samme deklarative form som alt andet. Containerne bruger værtens netværk uden nogen publicerede porte overhovedet, hvilket fjerner spørgsmålet om firewall‑omgåelse frem for at afbøde det. Units kører på systemniveau frem for rootless, og det er noteret som en afvejning — det holder automatiseringen enkel og normal, på bekostning af den strengere isolation rootless ville give, og noten siger hvilken vej man skal gå, hvis containerudbrud nogensinde kommer til at betyde mere end enkel automatisering. Udrulning blev adskilt fra provisionering: root sætter udrulningsstien op én gang, og derefter genudruller en uprivilegeret bruger ved at køre ét fast script gennem en afgrænset regel, der tillader den kommando og ingen argumenter. Byg imaget, genstart tjenesten. Ingen root‑shell, ingen vilkårlige kommandoer.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Containere kører under den supervisor, der allerede var betroet, bag den firewall der allerede var erklæret, og en rutinemæssig release kræver ingen privilegeret adgang. Valget mod standarden er skrevet ned med sin begrundelse, hvilket betyder mere end valget — den næste person vil få at vide, at man skal bruge standarden, af hver eneste artikel de læser.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Containere (Docker/Kubernetes)","DevOps","Infrastruktur","Linux \u0026 servere","Sikkerhed","Cloud‑infrastruktur \u0026 migrering","Containerisering \u0026 orkestrering","DevOps \u0026 CI/CD‑automatisering","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/provisioned-a-second-server-from-code-123/","url":"https://platform.engineer.company/da/portfolio/provisioned-a-second-server-from-code-123/","title":"Har automatiseret provisioneringen af en anden server hos en anden cloududbyder og oprettet firewallen før maskinen, så den fødes bag en, med begge udbyderes firewalls skrevet direkte mod deres REST‑API'er for at undgå en tredjepartssamling.","summary":"En anden vært kan oprettes, eller genadopteres, fra repositoryet, bag en firewall der allerede findes, til en pris filen oplyser.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Der var brug for endnu en maskine til et applikationslag, hos en anden udbyder end den første, og den første maskines egen historie var argumentet for hvordan: den var blevet oprettet i hånden, og dens firewall var blevet tilføjet bagefter, hvilket efterlader et vindue, hvor en frisk vært med en standardadgangskodepolitik er tilgængelig fra internettet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Maskinen skulle oprettes fra repositoryet, og den skulle fødes bag sin firewall frem for at få en kort tid efter.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Rækkefølgen bar designet. Firewall‑playet kører før server‑playet, så regelsættet findes, før der er noget for det at beskytte; hos den anden udbyder kører tagging før cloud‑firewallen, fordi en firewall der retter sig mod tags har brug for, at taggene findes først. Begge udbyderes firewalls skrives direkte mod deres REST‑API\u0026rsquo;er gennem en generisk HTTP‑opgave frem for gennem en leverandørsamling, hvilket fjerner en afhængighed og dens versionsdrift på bekostning af at skrive forespørgselsformerne i hånden. Den rolle der opretter maskinen, adopterer en eksisterende frem for at duplikere den, hvis den allerede er der, så genkørsel er sikker. Den er også dokumenteret, i sin egen fil, som den ene rolle i repositoryet der bruger penge — instanstypen, regionen, specifikationen og den månedlige omkostning i euro står alle skrevet, for et play der sender nogen en regning, bør sige det, hvor de vil læse det. Det play er også det, der ikke kan afprøves på den sædvanlige måde: den generiske HTTP‑opgave erklærer ingen understøttelse af check‑tilstand, så en prøvekørsel springer hver opgave i den over. Afprøvningen sker mod firewall‑playet i stedet, hvilket er gratis og kan rulles tilbage.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En anden vært kan oprettes, eller genadopteres, fra repositoryet, bag en firewall der allerede findes, til en pris filen oplyser. Den ene ting prøvekørsel ikke kan dække, er navngivet i den samme fil frem for efterladt som en overraskelse.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Automatisering \u0026 CI/CD","Cloud","Infrastruktur","Python","Sikkerhed","Cloud‑infrastruktur \u0026 migrering","Infrastructure as Code","Netværk \u0026 VPN‑opsætning","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/wrote-a-scope-rule-after-a-credential-leak-124/","url":"https://platform.engineer.company/da/portfolio/wrote-a-scope-rule-after-a-credential-leak-124/","title":"Har skrevet en scoperegel ind i kodebasen, efter at en omstrukturering bar en anden virksomheds inventar, firewalltilladelser og prosa med ind i den — og har holdt det karantæneramte restmateriale under hemmelighedsscanneren frem for at udelukke det.","summary":"Omfangsgrænsen er en skreven regel med en oplyst test, resterne er synlige frem for begravet, og den konkrete form for legitimationsoplysning der slap igennem,…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Arbejde udført for en anden virksomhed kørte med gennem en omstrukturering af repositoryet og blev liggende. Det der fulgte med var ikke abstrakt: en kladdelog med levende legitimationsoplysninger i klartekst, som lå i træet forbi hver eneste vagtpost gennem det meste af repositoryets historie; en forældet inventarliste der navngav den virksomheds vært; og en firewall‑opgavefil der åbnede porte til seksten af dens kundenetværk. Intet af det kørte. Det er præcis derfor det overlevede — noget der ikke kører nogen steder, bliver aldrig gennemgået igen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Grænsen skulle blive til en regel med en test knyttet til sig frem for en hensigt, og resterne skulle håndteres på en måde, der ikke blot skjulte dem.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Reglen er nu åbningsafsnittet i repositoryets instruktionssæt: dette repository håndterer én virksomheds infrastruktur og kun den, og en anden parts vært, inventarliste, firewalltilladelse, DNS‑zone eller legitimationsoplysning hører ikke hjemme i det — ikke engang deaktiveret, udkommenteret eller parkeret i en fil, ingen playbook importerer. Den praktiske test står ved siden af: ville denne virksomhed stadig være ansvarlig for dette, hvis forholdet ophørte. Resterne blev sat i karantæne i et tydeligt navngivet katalog frem for slettet, så historikken forbliver læselig, og karantænen er bevidst delvis — de to strukturelle linters springer den over, og hemmelighedsskanneren, boksvagten og emoji‑tjekket læser den bevidst stadig, for det er de tre, der ville fange det, der slap ind. Selve lækagen frembragte en linterregel: et brugerdefineret mønster der markerer en legitimationsoplysning sendt som et kommandolinjeflag, hvilket er den form den lækkede havde, og som standardregelsættet ikke matchede.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Omfangsgrænsen er en skreven regel med en oplyst test, resterne er synlige frem for begravet, og den konkrete form for legitimationsoplysning der slap igennem, fejler nu et commit. Læren noteret ved siden af er den generelle: det farlige artefakt er ikke det der kører, det er det der ikke gør, for det er det, ingen læser igen.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Data governance","Dokumentation","Infrastruktur","Sikkerhed","Data governance \u0026 datakvalitet","Sikkerhed \u0026 adgangsstyring","Teknisk dokumentation"]},{"id":"https://platform.engineer.company/da/portfolio/split-every-operational-secret-per-host-125/","url":"https://platform.engineer.company/da/portfolio/split-every-operational-secret-per-host-125/","title":"Har delt de fire hemmeligheder, der hører til den enkelte vært, efter at have fastslået, at to værter, der deler én dødmandsknap, alarmerer mindre end to knapper, ikke mere, og at en delt backup‑adgangssætning gør to værter til ét repository.","summary":"Hemmeligheder er per vært ved konstruktion, og de to fejltilstande, der ville være fulgt af at dele dem, er skrevet ned, hvor den næste person redigerer filen.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Platformen var bygget til én vært og var ved at få to. Flere driftshemmeligheder var skrevet som enkeltværdier på den antagelse: ét sikkerhedskopi‑repository, én adgangssætning, én alarmswitch. At udvide dem til en anden vært ved at dele dem er vejen med mindst modstand, og den er forkert på to adskilte måder, som begge fejler tavst.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Hver hemmelighed skulle blive til et opslag med værten som nøgle, og begrundelsen skulle skrives ned, for den delte udgave ser korrekt ud og koster intet, indtil den dag det betyder noget.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e To argumenter afgjorde det, og begge er noteret ved siden af konfigurationen. En dead man\u0026rsquo;s switch delt af to værter alarmerer mindre end to switches, ikke mere: hvis blot den ene vært stadig pinger, holder den tjekket grønt mens den anden er død, så at føje en vært til en delt switch reducerer aktivt dækningen for den, der allerede var der. Og en sikkerhedskopi‑repositorystreng er kun en placering — adgangssætningen er hele krypteringen, så to værter der deler en adgangssætning, er ikke to repositorier med en fælles hemmelighed, de er ét repository med to kataloger i sig. Hver værdi blev til et opslag med inventarværtsnavnet som nøgle, resolveret per vært af en delt opgavefil, som både konvergeringen og preflight kører. Den eksisterende vært blev derefter navngivet i alle fire opslag med sit repository og begge switches efterladt tomme, hvilket er den sandfærdige tilstand og holder de to åbne initiativer ærlige om, at de skylder operatøren halvdelen af arbejdet frem for koden.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Hemmeligheder er per vært ved konstruktion, og de to fejltilstande, der ville være fulgt af at dele dem, er skrevet ned, hvor den næste person redigerer filen. De tomme poster er den del, der er værd at beholde: de siger, at ledningsføringen findes og værdien ikke gør, hvilket er et andet og mere nyttigt udsagn end en manglende nøgle.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Drift \u0026 backup","Infrastruktur","Linux \u0026 servere","Løsningsarkitektur","Sikkerhed","Backup \u0026 disaster recovery","Infrastructure as Code","Sikkerhed \u0026 adgangsstyring","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/built-a-22-linter-commit-gate-127/","url":"https://platform.engineer.company/da/portfolio/built-a-22-linter-commit-gate-127/","title":"Har bygget en commit‑gate af 22 en‑linjes lintere plus fem, der fortjener et afsnit, uden et advarselsniveau og uden tilladte inline‑undertrykkelser, med dækning af HTML, CSS, JavaScript, Python, YAML, Markdown, shell, links, stavning, hemmeligheder og typografi.","summary":"Mekaniske indvendinger fremsættes af en maskine, før et commit findes, og gaten er den eneste anmelder dette projekt har.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En bidragsvejledning er et sæt forslag. Alle er enige i den, og så er det fredag, ændringen er lille, og vejledningen taber. På et enmandsprojekt er det værre frem for bedre, for der er slet ingen anmelder — det eneste mellem en dårlig ændring og produktion er den person der skrev den, i det øjeblik hvor de er mindst tilbøjelige til at skændes med sig selv.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Standarderne skulle være eksekverbare, så at bryde en fejler et commit frem for at vente på at blive bemærket.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Det der voksede frem er en gate på 22 linters uden advarselsniveau: hver diagnostik er en fejl, og sitegeneratoren selv kører med advarsler forfremmet til fejl, så selv en udfasning standser bygningen. De oplagte er der — HTML‑validering, CSS, JavaScript, Python, YAML, Markdown, shell, stavning, hemmeligheder, døde links. De interessante er de projektspecifikke tjek, som intet standardværktøj har en holdning til: at begge farvetemaer maler hver lagdelt flade med det samme antal lag, at intet fotografi vises bredere end halvdelen af sine kildepixels, at det udrullede træ ikke indeholder nogen privat sti eller værtsnavn, at teksten adlyder toneregelsættet på alle tre sprog, at en side har en Markdown‑tvilling og en gyldig repræsentation i et alternativt protokolformat. Inline‑undertrykkelser er direkte forbudt — ingen ignorér‑kommentar, intet deaktiveringsdirektiv, ingen omgåelse af hooket og ingen omdøbning af en fil for at undvige en matcher. Reglen der holder det ærligt er, at en allerede eksisterende fejl ikke er en undskyldning: et tjek der bringer en defekt frem, som ingen indførte, bliver rettet i samme omgang.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Mekaniske indvendinger fremsættes af en maskine, før et commit findes, og gaten er den eneste anmelder dette projekt har. Prisen er oplyst frem for skjult: det er langsomt at committe, og en dårligt skrevet vagtpost er oprigtigt irriterende at arbejde uden om, hvilket er grunden til at vagtposterne selv senere kom under en formatter og en linter af deres egen.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Frontend‑udvikling","Python","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Produktstrategi \u0026 kravspecifikation","Teknisk dokumentation"]},{"id":"https://platform.engineer.company/da/portfolio/cut-the-visual-quality-gate-to-ten-minutes-128/","url":"https://platform.engineer.company/da/portfolio/cut-the-visual-quality-gate-to-ten-minutes-128/","title":"Har skåret sitets browserdrevne kvalitetsgate fra 1.636 sekunder til 615 ved at planlægge dens tjek længst‑først gennem en worker‑pulje begrænset til fire baner, efter at have målt at alfabetisk rækkefølge kostede 320 sekunder mod 224.","summary":"Gaten gik fra 1.636 sekunder til 615, mens tjekkene blev bredere frem for tyndere — den serielle omkostning steg, og den samlede tid faldt.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Elleve af sitets tjek driver en browser uden grænseflade: layout ved hver vinduesform designet tegner, typeskala, udvidelse af oversat tekst, kontrast, tilgængelighedsregler, tvungne farver, maskotstørrelse, bevægelse, print på tværs af seks papirkombinationer, konsolfejl og visuel regression. Kørt én ad gangen tog de 1.636 sekunder, lidt over syvogtyve minutter. En gate der tager syvogtyve minutter er en gate der bliver sprunget over, og et oversprunget tjek kan ikke skelnes fra et der består.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Den samlede tid skulle ned langt nok til, at det at køre dem var standarden frem for en beslutning, uden at svække nogen af dem.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Arbejdet var måling først. Hvert tjek blev tidsmålt enkeltvis på en maskine med tolv kerner: layout på 405 sekunder, type på 301, oversættelse på 282, kontrast på 189, tilgængelighed på 178, og så videre ned til sytten. To fund formede svaret. At køre dem alle på én gang var langsommere end at køre fire ad gangen — 265 sekunder mod 224 — for hvert tjek er selv en browser der arbejder parallelt, og at overbelaste maskinen koster mere, end samtidigheden vinder. Og at ordne efter længste behandlingstid først slog alfabetisk rækkefølge med næsten en tredjedel, 224 sekunder mod 320, hvilket er det klassiske planlægningsresultat og viser sig her, fordi tjekkene varierer med en faktor tyve i omkostning. Så kørslen er en afgrænset arbejderpulje, dimensioneret ud fra kerneantallet med et gulv på to og et loft på fire, fodret med det længste først. Ved siden af den blev tjekkene udvidet frem for indsnævret: de deler nu én viewport‑tabel med toogtyve vinduesformer, udledt af hver medieforespørgsel stylesheetet faktisk indeholder, hvilket tog layouttjekket alene fra 95 sekunder til 405.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Gaten gik fra 1.636 sekunder til 615, mens tjekkene blev bredere frem for tyndere — den serielle omkostning steg, og den samlede tid faldt. Målingen er den del der er værd at beholde: to fornuftigt lydende valg, at køre alt på én gang og at køre tingene i den rækkefølge de blev skrevet, var hver især målbart dårligere end alternativet.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Frontend‑udvikling","Performanceoptimering","Python","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/mirrored-the-site-to-gemini-and-gopher-137/","url":"https://platform.engineer.company/da/portfolio/mirrored-the-site-to-gemini-and-gopher-137/","title":"Har spejlet hele sitet som 777 Gemini‑dokumenter og 777 Gopher‑dokumenter ud fra det samme udrullede træ, med nul bytes ændring i HTML'en.","summary":"Sitet kan læses over fire protokoller fra én bygning, med 777 dokumenter hver og nul ændring i HTML'en, og de alternative repræsentationer koster omkring tolv…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Sitet er statisk, har ingen sporing og ingen backend der kører, og dets argument er, at et dokument ikke har brug for en megabyte JavaScript for at blive læst. Det argument er let at fremføre og svært at påvise. To små internetprotokoller påviser det direkte, for ingen af dem kan bære et script overhovedet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Hele sitet skulle udgives over Gemini og Gopher fra det samme indhold, uden et andet indholdstræ og uden at ændre HTML\u0026rsquo;en.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Begge er outputformater af den samme bygning frem for en særskilt pipeline. Sitegeneratoren fik brugerdefinerede medietyper og outputformater, og hver side, hvert afsnit og hvert taksonomibegreb fik to repræsentationer mere ved siden af sin HTML og sin Markdown‑tvilling. Resultatet er 777 Gemini‑dokumenter og 777 Gopher‑menuer frembragt fra den samme kilde, udrullet til det samme træ og leveret af to små dæmoner på den samme vært. To egenskaber gjorde det værd at gøre frem for en kuriositet. Begge formater er markeret som ikke‑alternative repræsentationer, så intet ved HTML\u0026rsquo;en ændrede sig — ikke én byte, og det blev målt frem for antaget. Og Gopher‑formatet har ingen måde at lægge et link inde i en sætning, eftersom en menulinje er tabulatoradskilte felter, så teksten hårdombrydes ved otteogtres kolonner under bygningen; den begrænsning skærpede skrivningen på en måde, HTML\u0026rsquo;en aldrig krævede. Et Tor‑onion‑spejl ligger ved siden af dem, og det er den eneste offentlige flade platformen kunne tilføje uden at åbne en firewallport, for dæmonen ringer ud, og intet ringer ind.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Sitet kan læses over fire protokoller fra én bygning, med 777 dokumenter hver og nul ændring i HTML\u0026rsquo;en, og de alternative repræsentationer koster omkring tolv procent af HTML\u0026rsquo;ens egen vægt på disken. Kun én af de fire måles for trafik, og det står i platformens eget statistikdokument frem for at blive tavst ignoreret.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Frontend‑udvikling","Infrastruktur","Internationalisering","Webudvikling","DevOps \u0026 CI/CD‑automatisering","Internationalisering \u0026 lokalisering","Webstedsudvikling \u0026 CMS"]},{"id":"https://platform.engineer.company/da/portfolio/fixed-a-sitemap-with-one-shared-timestamp-140/","url":"https://platform.engineer.company/da/portfolio/fixed-a-sitemap-with-one-shared-timestamp-140/","title":"Har rettet et sitemap, hvor 172 af 176 URL'er delte ét ændringstidsstempel, ved at tage datoen fra git‑historikken efter at have fastslået, at eksporten omskriver hver fil ved hver kørsel.","summary":"En gensynkronisering efter en måneds indholdsredigeringer rører nu otte filer af 186 i stedet for dem alle, og sitemappet siger noget sandt.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Sitets sitemap fortalte søgemaskiner, at 172 af dets 176 sider sidst var ændret i det samme øjeblik. Det er ikke en subtil unøjagtighed — en ændringsdato er et løfte til en crawler om, at en sides indhold ændrede sig, og et site der giver det løfte 172 gange samtidig, fortæller enten sandheden om en fuld omskrivning eller fortæller ingen noget brugbart.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Datoerne skulle beskrive indholdet frem for filen, uden at opfinde en præcision repositoryet ikke har.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Årsagen var, at datoen kom fra filens ændringstidspunkt, og indholdseksporten omskriver hver fil ved hver kørsel, så en enkelt synkronisering stemplede hele korpuset. Rettelsen flytter kilden til versionshistorikken med en faldbagkæde, der først prøver et eksplicit felt, så commit‑historikken, så filen. Fælden i den rettelse er værd at notere: det bogstavelige feltnavn skal optræde på listen, ellers læser generatoren det aldrig, så en konfiguration der ser ud til at foretrække en skreven dato, men udelader navnet, ignorerer tavst hver skreven dato. To andre datobeslutninger kom ud af det samme arbejde. Databasen bærer nu hvornår hver post blev skrevet og sidst revideret, holdt bevidst adskilt fra hvornår arbejdet skete — de ligger år fra hinanden, og at slå dem sammen ville datere en side skrevet i år til et årti siden. Og rækker i den fil er valgfrie, og intet udledes, for repositoryets egen historik begynder efter indholdet gjorde: en manglende dato lader feltet stå tomt frem for at registrere en migrering, ud fra det princip at et præcist forkert tal er værre end et fraværende, eftersom det kun er det forkerte der bliver troet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e En gensynkronisering efter en måneds indholdsredigeringer rører nu otte filer af 186 i stedet for dem alle, og sitemappet siger noget sandt. Den generelle regel kom i metadatadokumentet ved siden af, for den samme fælde gælder hvert felt der har en faldbagkæde.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Brand \u0026 marketing","Python","Test \u0026 QA","Webudvikling","Brand, marketing \u0026 SEO","DevOps \u0026 CI/CD‑automatisering","Webstedsudvikling \u0026 CMS"]},{"id":"https://platform.engineer.company/da/portfolio/brought-the-quality-gate-code-under-a-linter-143/","url":"https://platform.engineer.company/da/portfolio/brought-the-quality-gate-code-under-a-linter-143/","title":"Har bragt 10.242 linjer kvalitetsgate‑JavaScript under en formatter og en linter efter at have fastslået, at det var kodebasens største kodemængde og den eneste, intet læste, og har rettet 13 fund uden at undertrykke nogen.","summary":"Den største og mest bærende kode i repositoryet er nu formateret, lintet og typetjekket, med tretten fund rettet og nul undertrykkelser.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Sitets kvalitetsgate er toogtredive scripts på i alt 10.242 linjer JavaScript. Den var med god margin den største kodemængde i repositoryet, og den var den eneste kodemængde, intet læste — ingen formatter, ingen linter, ingen typetjek. De programmer der håndhævede hver regel i projektet, var de eneste programmer, der var underlagt ingen af dem.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Tjekkerne skulle holdes til den standard, de findes for at håndhæve, og formatteren skulle tilpasses dem frem for omvendt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Formatteren kom først, og indrykningsbredden var den interessante beslutning. Repositoryets standard er fire mellemrum; ved fire mellemrum ville formatteren have omskrevet 2.173 linjer af den største tjekker. En overstyring til to mellemrum for de filer reducerede det til 38 linjers ægte drift. Princippet noteret sammen med den er, at formatteren tilpasses koden, ikke koden formatteren — at omformatere to tusind linjer for at tilfredsstille en præference ødelægger evnen til at læse filens historik. Derefter linteren, som frembragte tretten reelle fund, alle rettet og ingen undertrykt. Python‑tjekkerne fik den samme behandling gennem et typetjek konfigureret på et mellemniveau af stringens med tredive enkeltstående regler fra det strengeste niveau slået til ovenpå, valgt ved måling: ved den indstilling er træet tavst, og af de tredive kandidater var niogtyve allerede tavse, og én udløste — og den blev rettet frem for undtaget. Typetjekket fandt to reelle defekter, som linteren havde ladet bestå rent, begge om en værdis form frem for dens syntaks.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Den største og mest bærende kode i repositoryet er nu formateret, lintet og typetjekket, med tretten fund rettet og nul undertrykkelser. Grunden til at det betyder mere, end linjeantallet antyder, står i planen: en defekt i sitets stylesheet viser sig som en side der ser forkert ud, og en defekt i en tjekker viser sig som et tjek der består, når det ikke burde.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Frontend‑udvikling","Python","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/held-the-generator-to-964-tests-and-a-coverage-floor-145/","url":"https://platform.engineer.company/da/portfolio/held-the-generator-to-964-tests-and-a-coverage-floor-145/","title":"Har holdt generatoren til 981 testtilfælde med et dækningsgulv på 92 % grendækning og advarsler behandlet som fejl, og har hævdet idempotens ved at køre hele build‑pipelinen to gange fra en tom fil og kræve, at anden gennemløb intet ændrer.","summary":"Pipelinen kan køres igen mod en levende database uden frygt, hvilket er det der overhovedet gør trinvist indholdsarbejde muligt.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e En generator der samler en database fra bunden, har en særlig slags fejl: den virker første gang og korrumperer anden gang. Trin der indsætter uden at tjekke, trin der afhænger af rækkefølgen i et tidligere trins output, trin der er sikre alene og ikke sammen. Intet af det viser sig i en test, der starter fra ingenting og kører én gang.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Bygningen skulle bevises gentagelig frem for blot fungerende, og testsuiten skulle være stor nok og streng nok til, at en regression ikke kunne slippe tavst igennem den.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Idempotenstesten er den stumpe og den mest nyttige: byg hele databasen fra en tom fil, tag et øjebliksbillede, kør hele pipelinen i nitten trin igen over resultatet, og kræv at anden gennemløb ikke ændrer noget. Rækketal, indhold og identifikatorer skal alle stemme. Omkring den ligger 383 testfunktioner — 981 tilfælde når de parametriserede folder sig ud — fordelt på femogfyrre filer og 7.944 linjer, der dækker pipelinen, gengiverne, indholdsindlæserne, målretningslogikken og tjekkerne. Grendækning bærer et gulv på tooghalvfems procent håndhævet i bygningen frem for rapporteret i et sammendrag, og suiten måler i øjeblikket omkring 95 procent, så gulvet har luft uden at være dekorativt. Advarsler er konfigureret som fejl, hvilket er den indstilling der betyder mest i praksis — en udfasningsmeddelelse der udskrives i to år, er en udfasningsmeddelelse ingen læser, og den kørsel der forvandler den til en rød test, er den kørsel der får den rettet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Pipelinen kan køres igen mod en levende database uden frygt, hvilket er det der overhovedet gør trinvist indholdsarbejde muligt. Gulvet er et gulv, ikke et mål, og det er værd at sige, at tooghalvfems procent grendækning stadig efterlader grene, intet nogensinde har taget — tallet afgrænser risikoen, det fjerner den ikke, og to af de defekter der senere blev fundet i projektet, lå i kode, dækningsrapporten viste som dækket.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Databaser","DevOps","Drift \u0026 backup","Python","Test \u0026 QA","Backend- \u0026 API‑udvikling","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/enabled-every-python-linter-rule-as-an-error-147/","url":"https://platform.engineer.company/da/portfolio/enabled-every-python-linter-rule-as-an-error-147/","title":"Har valgt hver eneste regel, Python‑linteren har, som fejl og har arbejdet 1.815 fund ned til nul, hvor hver af de få undtagelser bærer en skreven begrundelse og to af dem er understøttet af et tjek frem for en kommentar.","summary":"Linteren kører ved fuld styrke med nul fund, og hver afvigelse er dokumenteret på den linje, hvor den tages.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e De fleste projekter vælger en behagelig delmængde af deres linters regler, og delmængden vælges af hvilke regler der var tavse den dag, den blev konfigureret. Det gør konfigurationen til en optegnelse over kodens eksisterende vaner frem for en standard koden holdes til, og hver regel der er slået fra, er en defektklasse, ingen nogensinde får besked om.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Standarden skulle vendes om — hver regel værktøjet implementerer slået til som en fejl — og den resulterende efterslæb arbejdes ned til nul frem for forhandlet ned ved at slå regler fra igen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e At vælge det komplette regelsæt frembragte 1.815 fund ved første kørsel. De blev arbejdet igennem efter kategori frem for efter fil, for kategorierne fortæller noget: ubrugte argumenter og skyggede indbyggede navne er støj, men sikkerhedskategorien, kategorien for foranderlige standardværdier og kategorien for undtagelseshåndtering pegede hver især på reel adfærd. Der findes ægte uforeneligheder — en formatter og en linter kan være uenige om den samme linje, og nogle få regler modsiger projektets egne bevidste valg — og hver af det lille antal undtagelser bærer en skreven begrundelse dér hvor undtagelsen tages, som siger hvad reglen ville, og hvorfor denne kode gør noget andet. To af dem går videre og er understøttet af et tjek frem for en kommentar, så undtagelsen ikke tavst kan udvide sig: reglen er slået fra, og en test efterprøver netop den egenskab, reglen ville have håndhævet. Typetjek kører i streng tilstand ved siden af, hvilket er en særskilt og hårdere standard, og det er den, der fangede defekter, linteren ikke kunne se, fordi de handler om hvad en værdi er frem for hvordan den er skrevet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Linteren kører ved fuld styrke med nul fund, og hver afvigelse er dokumenteret på den linje, hvor den tages. Prisen er reel og værd at nævne: den strengeste indstilling frembringer fund, der oprigtigt ikke er værd at handle på, og nogen skal træffe den vurdering 1.815 gange frem for én gang.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Python","Test \u0026 QA","Backend- \u0026 API‑udvikling","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/wrote-tests-for-the-checkers-themselves-149/","url":"https://platform.engineer.company/da/portfolio/wrote-tests-for-the-checkers-themselves-149/","title":"Har skrevet tests af selve tjekkene efter at have fastslået, at et tjek, der kun fodres med rent input, en dag melder rent, fordi det intet læste — ved at plante en stavefejl for at bekræfte, at stavekontrollen finder den, og ved at tage et id‑interval fra databasen frem for fra et tal i testen.","summary":"Hvert tjek i projektet har nu en test, der beviser, at det fejler på dårlige inddata, og områdeforventningerne læses fra sandhedskilden.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Projektet kører en række egne tjek over sit eget indhold — en stavekontrol, et tjek af identifikatorernes talområde, et stemmetjek, et tjek af taloverensstemmelse. Hvert af dem havde kørt grønt i månedsvis. Et tjek, der kun nogensinde har set rene inddata og kun nogensinde har meldt rent, kan ikke skelnes fra et tjek, der slet intet læser, og der fandtes ingen test i suiten, som kunne kende forskel på de to.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Tjekkene skulle bringes til at bevise, at de kan fejle, og de fikstursdata, de tjekker imod, skulle holde op med at være håndholdte kopier af det, de beskriver.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Mønsteret, der er anvendt hele vejen igennem, er at plante netop den defekt, tjekket findes for at fange, og kræve at tjekket finder den. Stavekontrollen fodres med et bevidst fejlstavet ord, og testen fejler, hvis kørslen kommer rent tilbage. Stemmetjekket fodres med prosa i første person og skal afvise den. Buzzword‑tjekket fodres med et forbudt ord. Hvert af disse er en lille test, og hver af dem lukkede en reel blind vinkel, for to af tjekkene viste sig at læse et snævrere sæt filer end deres dokumentation påstod og havde tavst sprunget indhold over. Den anden ændring handler om, hvor en test henter sine forventninger: tjekket af identifikatorernes talområde sammenlignede før med et tal skrevet i testfilen, hvilket betød, at enhver tilføjelse af indhold krævede en redigering af en test, og en redaktør, der opdaterede tallet uden at se efter, havde slået tjekket fra. Det udleder nu området fra databasen, så tjekket beskriver dataene frem for en forældet erindring om dem.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Hvert tjek i projektet har nu en test, der beviser, at det fejler på dårlige inddata, og områdeforventningerne læses fra sandhedskilden. Det ubehagelige er, hvad dette blotlagde: et grønt tjek havde været meningsløst mindst to steder i et ukendt tidsrum, og der er ingen måde at finde ud af med tilbagevirkende kraft, hvad der slap igennem.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Data governance","Databaser","Python","Test \u0026 QA","Data governance \u0026 datakvalitet","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/found-the-commit-hooks-and-the-gate-disagreeing-150/","url":"https://platform.engineer.company/da/portfolio/found-the-commit-hooks-and-the-gate-disagreeing-150/","title":"Har fundet commit‑hooks og kvalitetsgaten køre forskellige tjek, mens et dokument lovede, at de var de samme, ved at sammenligne de to lister i en test — de fem, der kun nogensinde kørte i hånden, var dem, der læste CV‑prosaen.","summary":"Hooken og porten kører beviseligt de samme tjek, og prosatjekkene kører nu ved hver commit. Afvejningen er commit-hookens køretid, som voksede og vil blive ved…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Projektet havde en commit‑hook, der kører tjek, før en commit accepteres, og en fuld kvalitetsport kørt efter behov. Et dokument angav, at hooken kører porten, så intet kunne nå historikken uden at bestå alt. Begge lister blev vedligeholdt i hånden, i to forskellige filer, og intet sammenlignede dem.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Påstanden skulle gøres til en efterprøvning, hvilket betød at opregne begge mængder programmatisk og fejle, når de afviger.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Testen læser hook‑konfigurationen og portens opgavedefinitioner, opløser hver af dem til den mængde tjek, den faktisk kalder, og sammenligner. De stemte ikke. Fem tjek fandtes kun i porten og kørte aldrig ved commit, og de fem var ikke tilfældige — det var dem, der læser selve CV\u0026rsquo;ets prosa: stemmetjekket, der holder anmeldelser upersonlige, buzzword‑tjekket, tjekket af taloverensstemmelse på tværs af sprog, notationstjekket og stavekontrollen over indhold. Med andre ord kørte hvert tjek, der beskytter kode, automatisk, og hvert tjek, der beskytter skriften, kørte kun, når nogen huskede det. Da skriften er hele produktet, var udsattheden vendt om i forhold til, hvor nogen ville have gættet. Rettelsen var at bringe de fem ind i hooken, hvilket krævede at gøre to af dem hurtige nok til at overleve et budget før commit, og derefter at holde sammenligningstesten på plads, så de to lister ikke kan glide fra hinanden igen. Dokumentet, der havde beskrevet en hensigt, beskriver nu noget håndhævet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Hooken og porten kører beviseligt de samme tjek, og prosatjekkene kører nu ved hver commit. Afvejningen er commit‑hookens køretid, som voksede og vil blive ved med at vokse, efterhånden som indholdet vokser, og der findes et punkt, hvor en langsom hook bliver omgået — så denne rettelse har en holdbarhed målt i, hvor længe tjekkene forbliver hurtige.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Dokumentation","Python","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Teknisk dokumentation","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/rehearsed-the-forge-side-ci-hook-151/","url":"https://platform.engineer.company/da/portfolio/rehearsed-the-forge-side-ci-hook-151/","title":"Har prøvekørt CI‑hooken på forge‑siden og har fundet to fejl, der ikke kunne nås ved at læse filen: en fallback, der lagde et uopløseligt argument på hookens input, og gits egen miljøvariabel, der fulgte gaten ind i checkouten og gjorde 19 tests røde.","summary":"Hooken virker på begge veje, og begge defekter blev fundet, før noget rigtigt push mødte dem. Begrænsningen er, at en prøve stadig er en simulering: den…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Den selvhostede git‑forge tager imod pushes og kan køre en hook på serversiden for at afvise arbejde, der ikke består porten. En hook på serversiden er det ene stykke automatik, der ikke kan testes ved at køre den lokalt: den udføres i et bart lager, uden arbejdstræ, under et miljø, forgen sætter, og læser de pushede referencer fra sin standardinddata. At læse skriptet og konkludere, at det er korrekt, er et gæt.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Hooken skulle prøves af mod et rigtigt bart lager og et rigtigt push, før den blev betroet, frem for at blive udrullet og opdaget.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Prøven opretter et bart lager, installerer hooken, kloner det, committer, pusher og efterprøver både accept- og afvisningsvejen. Den fandt to defekter, hvoraf ingen af dem var synlige i filen. Den første var en nødløsning i argumenthåndteringen: når hooken ikke kunne bestemme en reference, indsatte den en pladsholder, der ikke var et opløseligt objekt, og fordi værdien ankommer på hookens standardinddata frem for som en parameter, viste fejlen sig som en urelateret fejl meget længere nede. Den anden er den, der er værd at huske. Git eksporterer en miljøvariabel, der navngiver lagermappen, når den kalder en hook, og den variabel nedarves af alt, hvad hooken kører. Porten tjekker den pushede revision ud og kører testsuiten inde i den, og testsuiten nedarvede pegepinden til det bare lager — så nitten test, der rører git, opløste mod det forkerte lager og gik røde på kode, der var korrekt. Miljøet skal ryddes ved grænsen, og den grænse er usynlig, medmindre tingen faktisk køres.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Hooken virker på begge veje, og begge defekter blev fundet, før noget rigtigt push mødte dem. Begrænsningen er, at en prøve stadig er en simulering: den beviser, at hooken overlever én pushform, og forgen ser i drift pushes, som denne prøve ikke konstruerer, herunder tvungne opdateringer og sletning af grene.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Infrastruktur","Linux \u0026 servere","Python","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Infrastructure as Code","Systemadministration"]},{"id":"https://platform.engineer.company/da/portfolio/stopped-an-application-filling-memory-at-41mb-a-second-155/","url":"https://platform.engineer.company/da/portfolio/stopped-an-application-filling-memory-at-41mb-a-second-155/","title":"Har stoppet en applikation i at fylde hukommelsen med 41 MB i sekundet — registrerede 111 GB komprimerede sider på en maskine med 36 GB — ved at begrænse hver hændelsesstrøm, abonnere efter hændelsestype og lægge et ratebudget på logning, hvilket tog 610.996 loglinjer ned til 1.411.","summary":"Hukommelsen holder sig flad under vedvarende belastning, og den samme session, der frembragte 610.996 loglinjer, frembringer 1.411.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Programmet fyldte hukommelsen, indtil styresystemet slog det ihjel. Den registrerede hændelse nåede 111 GB komprimerede sider på en maskine med 36 GB, stigende med omkring 41 MB i sekundet, og logfilen for en enkelt kort session rummede 610.996 linjer. En maskine i den tilstand er ikke langsom, den er ubrugelig — nedlukningen kommer, efter at swap allerede har fået alt andet på skrivebordet til at holde op med at svare.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Væksten skulle findes frem for gættes på, og hver ubegrænset vej skulle gives en grænse, for én afgrænset kø ved siden af tre uafgrænsede er ikke en rettelse.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Der var tre gangende årsager, og gangningen er grunden til, at det gik så hurtigt. Hændelsesstrømmen fra kernen blev forbrugt uden nogen grænse, så hændelser ankom hurtigere, end grænsefladen kunne anvende dem, og efterslæbet blev bevaret frem for kasseret. Hver abonnent modtog hver hændelse og filtrerede bagefter, så omkostningen ved én hændelse blev ganget med antallet af lyttere, og hver lytters filtrering allokerede. Og logning var ubudgetteret, så hver hændelse frembragte loglinjer — hvilket er det sammensatte led, for logningens omfang var proportionalt med omfanget af det, der gik galt. Rettelsen tog fat på alle tre: afgrænsede buffere med en udtrykkelig politik for, hvad der sker når de fyldes, abonnement efter hændelsestype så en lytter kun vækkes for hændelser, den vil have, og et hastighedsbudget på logning, der falder gentagelser sammen frem for at skrive hver enkelt. En regressionstest driver en høj hændelseshastighed og efterprøver, at hukommelsesloftet holder, så grænsen er en egenskab ved bygningen frem for en kommentar.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Hukommelsen holder sig flad under vedvarende belastning, og den samme session, der frembragte 610.996 loglinjer, frembringer 1.411. Den ærlige bemærkning er, at hastighedsbudgettet på logning kasserer information: når noget går galt hurtigt nu, er optegnelsen over det bevidst ufuldstændig, og det er en handel indgået med åbne øjne mod alternativet, en maskine der går i stå.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Drift \u0026 backup","Frontend‑udvikling","Løsningsarkitektur","Monitorering \u0026 observability","Performanceoptimering","Test \u0026 QA","Platform- \u0026 løsningsarkitektur","Site reliability \u0026 monitorering"]},{"id":"https://platform.engineer.company/da/portfolio/wrote-a-parser-that-verifies-every-ffi-call-site-157/","url":"https://platform.engineer.company/da/portfolio/wrote-a-parser-that-verifies-every-ffi-call-site-157/","title":"Har skrevet en parser, der læser den rigtige C‑header på 7.308 linjer og verificerer hvert kaldssted, hver enum‑konstant og at hver pegerejende klasse er final, efter at en håndskrevet pladsholder‑header lod kald til tre fjernede funktioner kompilere, linke og crashe.","summary":"Den defektklasse, der frembragte det oprindelige nedbrud, kan ikke gentage sig, for en forældet henvisning er nu en bygningsfejl frem for en kørselsfejl.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Tidligt i projektet blev C‑grænsefladen repræsenteret af en håndskrevet header, der beskrev de funktioner, programmet forventede. Den header oversatte, programmet blev sammenkædet, og kald til tre funktioner, der ikke længere fandtes i kernen, nåede frem til at blive kaldt og brød ned. Både oversætteren og sammenkæderen var blevet tilfredsstillet af en beskrivelse af biblioteket frem for af biblioteket, og kløften viste sig først ved kørsel.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Den rigtige header — 7.308 linjer og 268 erklæringer — skulle blive myndigheden, og hver anvendelse af den i Swift‑koden skulle efterprøves mod den automatisk frem for ved gennemgang.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Tjekket er en parser, der læser den faktiske opstrøms‑header og opbygger mængden af funktioner, enum‑konstanter og typer, den erklærer, og derefter læser Swift‑kildekoden og opløser hvert kaldested og hver konstanthenvisning mod den mængde. Et kald til en funktion, headeren ikke erklærer, fejler bygningen. En henvisning til en enum‑konstant, der er blevet omdøbt, fejler bygningen. Det nuværende tal er 132 af de 268 erklæringer henvist til, og at vide hvilke 136 der er ubrugte, er i sig selv nyttigt, for det siger præcis, hvor meget af kernen klienten ikke har nået. Parseren håndhæver også en regel, oversætteren ikke kan: hver Swift‑klasse, der ejer en pegepind ind i kernen, skal være endelig. En ikke‑endelig klasse, der ejer en pegepind, kan nedarves, og en underklasse, der tilsidesætter deinitialisering eller tilføjer sin egen levetid, ændrer, hvornår pegepinden frigives — en brug efter frigivelse uden noget usikkert nøgleord i nærheden. Reglen tjekkes ved navn på tværs af hele træet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Den defektklasse, der frembragte det oprindelige nedbrud, kan ikke gentage sig, for en forældet henvisning er nu en bygningsfejl frem for en kørselsfejl. Begrænsningen er, at parseren forstår headerens erklæringer og ikke dens betydning: den beviser, at en funktion findes med et matchende navn, og en funktion, hvis betydning eller ejerskabsregel ændrede sig opstrøms, mens signaturen blev bevaret, slipper igennem uden bemærkning.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Automatisering \u0026 CI/CD","Backend‑udvikling","Dokumentation","Sikkerhed","Test \u0026 QA","Backend- \u0026 API‑udvikling","DevOps \u0026 CI/CD‑automatisering","Teknisk dokumentation"]},{"id":"https://platform.engineer.company/da/portfolio/took-the-test-suite-under-nine-seconds-158/","url":"https://platform.engineer.company/da/portfolio/took-the-test-suite-under-nine-seconds-158/","title":"Har taget testsuiten fra seks tests over en grænse på tres sekunder til 135 beståede på 8,9 sekunder ved at profilere hovedtråden og fjerne de to kald, den sad inde i i 3.989 ud af 4.017 stikprøver.","summary":"Suiten gik fra seks test over tres sekunder — 74 sekunders vægur — til 135 test bestået på 8,9, hvilket bringer den inden for det vindue, hvor den kører ved…","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Testsuiten havde en tidsgrænse på tres sekunder, og seks test lå over den. En testsuite, der tager over et minut, holder op med at blive kørt før hver ændring, og en suite, der ikke køres før hver ændring, er en rapport om fortiden. Instinktet i den situation er at hæve grænsen, og at hæve grænsen er, hvordan en suite når ti minutter.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Tiden skulle findes frem for budgetteres til, hvilket betød at profilere suiten i stedet for at ræsonnere om, hvilke test der så dyre ud.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Profilen blev taget på hovedtråden, mens suiten kørte, og resultatet modsagde gættet. Hovedtråden sad inde i to kald i 3.989 stikprøver ud af 4.017 — så de langsomme test var ikke dem, der udførte mest arbejde, og næsten hver eneste test kom gennem de samme to steder. Det første var en fast ventetid brugt til at lade asynkront arbejde falde til ro før efterprøvning — reelt en søvn, betalt af hver test, der rørte hændelsesvejen, uanset om arbejdet allerede var færdigt. Den blev erstattet af at vente på den faktiske betingelse med en tidsfrist, så en test, der er klar på fem millisekunder, tager fem millisekunder, og kun en test, der reelt sidder fast, betaler den fulde ventetid. Det andet var opsætning pr. test, der genopbyggede et dyrt fikstur hver gang, hvor fiksturet var skrivebeskyttet og kunne bygges én gang for suiten. Ingen af dem lå i en test, nogen ville have udpeget som langsom; begge lå i den fælles vej, hvilket er grunden til, at hele suiten var ensartet langsom frem for at nogle få test var afvigere.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Suiten gik fra seks test over tres sekunder — 74 sekunders vægur — til 135 test bestået på 8,9, hvilket bringer den inden for det vindue, hvor den kører ved hver gemning. Forbeholdet er, at det fælles skrivebeskyttede fikstur nu er et koblingspunkt — en test, der ændrer det, vil frembringe en fejl i en anden test, og suitens hastighed afhænger af en disciplin, oversætteren ikke håndhæver.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","DevOps","Performanceoptimering","Test \u0026 QA","DevOps \u0026 CI/CD‑automatisering","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/portfolio/reached-the-unused-half-of-the-messaging-core-161/","url":"https://platform.engineer.company/da/portfolio/reached-the-unused-half-of-the-messaging-core-161/","title":"Har nået den halvdel af beskedkernen, applikationen aldrig havde brugt — backupoverførsel, forsvindende beskeder, redigering og gensendelse af beskeder, verificerede invitationer, proxyer og krypteringspolitik — og har drevet hver test mod det rigtige bibliotek uden mocks.","summary":"Den tidligere uopnåede halvdel af kernen drives af test mod det rigtige bibliotek, og indpakningen bærer det højeste gulv i projektet.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Klienten brugte omtrent halvdelen af det, beskedkernen tilbyder. Den ubrugte halvdel var ikke obskur — sikkerhedskopioverførsel mellem enheder, forsvindende beskeder, redigering og gensendelse af beskeder, verificerede indbydelseslinks, proxykonfiguration og krypteringspolitikken for en samtale. Hver af dem er en funktion, en bruger ville forvente, og hver af dem var et utestet område af C‑grænsefladen, hvilket er den farligere kendsgerning, for et utestet område af en C‑grænseflade er der, hvor ejerskabsfejlene bor.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Den uopnåede formåen skulle drives og dækkes, og testene skulle køre mod det rigtige bibliotek frem for mod en stedfortræder.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Reglen, der blev vedtaget, var ingen attrapper for kernen. En attrap af en C‑grænseflade indkoder udviklerens tro om, hvad biblioteket gør, og hver defekt værd at finde her er et sted, hvor den tro er forkert — så en bestået attrapbaseret test er bevis om attrappen. I stedet opretter testene rigtige konti i midlertidige mapper, driver det rigtige bibliotek og efterprøver, hvad det faktisk returnerer, hvor hver suite rydder op efter sin egen tilstand. Det var det, der gjorde dækningen meningsfuld: at udøve sikkerhedskopioverførsel betød at håndtere en rigtig overførsels tilstandsmaskine og dens fejlveje, og at udøve verificerede indbydelser betød at konstruere det rigtige linkformat og få biblioteket til at fortolke det. Dækningsgulve blev sat pr. lag frem for som ét tal, ved fireogfirs procent for kerneindpakningen, femoghalvfjerds for hjælpefunktioner og femogtres for modeller, ud fra den betragtning at det lag, der rører rå pegepinde, bør holdes højest, og at en model, der mest består af lagrede egenskaber, ikke bør polstres med test for at ramme et gennemsnit.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Den tidligere uopnåede halvdel af kernen drives af test mod det rigtige bibliotek, og indpakningen bærer det højeste gulv i projektet. Prisen er hastighed og forudsigelighed: test mod det rigtige bibliotek er langsommere end attrapper, og de kan fejle af miljømæssige grunde, hvilket er prisen for, at de kan fejle af rigtige.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["API'er \u0026 integration","Automatisering \u0026 CI/CD","Backend‑udvikling","Drift \u0026 backup","Sikkerhed","Test \u0026 QA","Backend- \u0026 API‑udvikling","DevOps \u0026 CI/CD‑automatisering","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/audited-644-rust-crates-for-licence-compatibility-163/","url":"https://platform.engineer.company/da/portfolio/audited-644-rust-crates-for-licence-compatibility-163/","title":"Har auditeret 644 Rust‑crates for licenskompatibilitet ved hvert build og har bevist, at tjekket udløses, ved at omskrive én crates licens og ved at flytte den fastlåste kernerevision uden at regenerere.","summary":"Distributionspositionen tjekkes ved hver bygning, og tjekket vides at virke, fordi det blev bragt til at fejle, frem for fordi det aldrig har sagt noget.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e At sammenkæde en Rust‑kerne ind i et udsendt program betyder at udsende alt, den kerne afhænger af. Afhængighedsgrafen er 644 crates. Hver af dem bærer en licens, nogle bærer mere end én, og en enkelt copyleft‑crate, der ankommer tre niveauer nede gennem en rutinemæssig versionsopdatering, ændrer, hvad programmet som helhed må distribueres under — tavst, i en låsefil ingen læser linje for linje.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Licenspositionen skulle efterprøves ved hver bygning frem for gennemgås én gang, med en udtrykkelig tilladelsesliste, så en ændring i grafen er en bygningsfejl og ikke en opdagelse gjort senere af en anden.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Revisionen opløser den fulde transitive graf og tjekker hver crates licensudtryk mod en liste over vilkår, projektet accepterer, og evaluerer de booleske udtryk korrekt — en crate, der tilbyder et valg mellem to licenser, er acceptabel, hvis en af dem står på listen, og en crate, der kræver begge, er kun acceptabel, hvis begge gør. Alt uden match fejler bygningen frem for at advare, og at føje et vilkår til tilladelseslisten er en bevidst redigering med en begrundelse. Et tjek, der aldrig har fejlet, kan ikke skelnes fra et, der ikke kan fejle, så det blev bragt til at fejle med vilje, to gange. Én gang ved at omskrive en crates licensudtryk til noget, listen ikke accepterer, hvilket er formen på en crate, der ændrer sine vilkår opstrøms mellem versioner — det tilfælde ingen menneskelig gennemgang fanger, fordi selve craten ikke er ny. Én gang ved at flytte den fastlåste kernerevision uden at genskabe revisionen, hvilket er formen på en kerneopdatering, der stille bringer en ny afhængighed med sig. Begge provokationer fejlede bygningen som tilsigtet, og tjekket blev efterladt på plads frem for udvidet.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Distributionspositionen tjekkes ved hver bygning, og tjekket vides at virke, fordi det blev bragt til at fejle, frem for fordi det aldrig har sagt noget. Før det fandtes, fremsatte pakken, licensfilen og projektets eget README alle en påstand om 644 crates, som intet havde efterprøvet. Begrænsningen er præcis og bør ikke overdrives: tjekket læser de erklærede licensmetadata, og metadata kan være forkerte eller ufuldstændige. Det beviser intet om en crate, der fejlerklærer sig selv, og det er ikke juridisk rådgivning.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Automatisering \u0026 CI/CD","Data governance","DevOps","Dokumentation","Sikkerhed","Data governance \u0026 datakvalitet","DevOps \u0026 CI/CD‑automatisering","Sikkerhed \u0026 adgangsstyring"]},{"id":"https://platform.engineer.company/da/portfolio/held-five-repositories-to-one-history-standard-165/","url":"https://platform.engineer.company/da/portfolio/held-five-repositories-to-one-history-standard-165/","title":"Har holdt fire kodebaser til én historikstandard — konventionel, uden emojis, uden attributionslinjer, håndhævet af en commit‑message‑hook — sammen med 46 instruktionsdokumenter, der styrer, hvordan arbejdet udføres.","summary":"Fem lagre deler ét historikformat og én anvisningsstruktur, begge håndhævet af hooks frem for af disciplin.","content_html":"\u003cp\u003e\u003cstrong\u003eSituation.\u003c/strong\u003e Fem lagre bygget over atten måneder af én person er den situation, hvor proces er lettest at springe over, for der er ingen at koordinere med, og prisen for en ulæselig historik betales helt af et fremtidigt jeg, der endnu ikke har beklaget sig. Det er også den situation, hvor en usammenhængende historik er mest sandsynlig, eftersom hvert lager kan glide ind i sine egne vaner uden noget, der trækker dem sammen.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOpgave.\u003c/strong\u003e Én standard for historik og én standard for anvisninger skulle gælde på tværs af hvert lager, arbejdet skrives i, og den skulle håndhæves maskinelt frem for huskes.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHandling.\u003c/strong\u003e Commit‑standarden er et konventionelt præfiks, der navngiver ændringens art, og et virkefelt, en emnelinje under en fast længde, ingen emojis og ingen tilskrivningslinjer af nogen art — det sidste fordi en linje, der krediterer et værktøj, ikke er en kendsgerning om ændringen, og historik er til kendsgerninger om ændringer. En hook på commit‑beskeden afviser alt, der ikke retter sig efter det, i hvert lager, der bærer arbejde, så standarden er en egenskab ved lageret frem for ved den, der committer. Fordelingen i det største lager viser, hvad arbejdet faktisk var: 156 funktions‑commits, 133 rettelser, 128 dokumentation, 81 husholdning, 26 omskrivninger, 20 stil, 5 ydeevne og 3 test. Dokumentation ligger tæt nok på rettelser til at være værd at bemærke, og det er en følge af den anden halvdel af dette — 46 anvisningsdokumenter på tværs af de fire, hvert dækkende ét område, hvert skrevet som regler frem for beskrivelse, alle nåelige fra ét indgangsdokument pr. lager, så der er ét sted at begynde. En dokumentationslinter håndhæver størrelsesbudgetter pr. fil og medlemskab af indekset, så mængden forbliver overskuelig frem for at vokse til et arkiv.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eResultat.\u003c/strong\u003e Fem lagre deler ét historikformat og én anvisningsstruktur, begge håndhævet af hooks frem for af disciplin. Hvad dette ikke gør, er at gøre historikken god: formatet tjekkes, og indholdet gør ikke, så en emnelinje, der retter sig efter formatet og beskriver intet, slipper igennem præcis lige så godt som en, der forklarer ændringen.\u003c/p\u003e\n","date_published":"2026-09-13T01:45:50+02:00","date_modified":"2026-09-13T01:45:50+02:00","language":"da","tags":["Agile \u0026 Scrum","Automatisering \u0026 CI/CD","DevOps","Dokumentation","Projektledelse","Teknisk ledelse","DevOps \u0026 CI/CD‑automatisering","Projektledelse (Agile)","Teknisk dokumentation","Teknisk ledelse \u0026 rådgivning"]},{"id":"https://platform.engineer.company/da/notes/solana-mobile-wallet-deeplinks/","url":"https://platform.engineer.company/da/notes/solana-mobile-wallet-deeplinks/","title":"Sådan åbner man en dApp i Solana-mobilwallets","summary":"Hvorfor browse-deeplinks til Phantom, Solflare og Backpack fejler på mobilen — og de formater, regler og rettelser, der får dem til at virke.","content_html":"\u003cp\u003eEn React-dApp bygget på \u003ccode\u003e@solana/wallet-adapter-react\u003c/code\u003e forbinder\ndesktop-wallets uden problemer, men på en telefon falder det samme flow fra\nhinanden: wallet\u0026rsquo;en skal åbne dApp\u0026rsquo;en i sin egen indbyggede browser, og de\ndeeplinks, der skulle klare det, virker bare ikke. Backpack lander på en \u0026ldquo;hent\nappen\u0026rdquo;-side; Solflare åbner appen, men aldrig sitet; alle varianter ser ud til\nat fejle. Vi skilte problemet ad, og det viste sig at være fire adskilte\nproblemer med ét fælles symptom.\u003c/p\u003e\n\u003ch2 id=\"de-fire-problemer\"\u003eDe fire problemer\u003c/h2\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eBackpack-linket var forkert bygget.\u003c/strong\u003e Det eneste dokumenterede format er\n\u003ccode\u003ehttps://backpack.app/ul/v1/browse/\u0026lt;url\u0026gt;?ref=\u0026lt;ref\u0026gt;\u003c/code\u003e — et universelt link med\nmål-URL\u0026rsquo;en i stien og et påkrævet \u003ccode\u003eref\u003c/code\u003e. Et gæt med eget skema som\n\u003ccode\u003ebackpack://ul/v1/browse?url=...\u003c/code\u003e matcher ingen rute i appen, så brugeren\nender på wallet\u0026rsquo;ens installationsside.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSolflare skal også bruge sit universelle link:\u003c/strong\u003e\n\u003ccode\u003ehttps://solflare.com/ul/v1/browse/\u0026lt;url\u0026gt;?ref=\u0026lt;ref\u0026gt;\u003c/code\u003e — ikke det rå\n\u003ccode\u003esolflare://\u003c/code\u003e-skema. Et råt skema kan starte appen uden at dirigere den —\nhvilket er præcis \u0026ldquo;appen åbner, men site-fanen må åbnes med hånden\u0026rdquo;.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eBegge parametre skal være kodet.\u003c/strong\u003e \u003ccode\u003eurl\u003c/code\u003e er dApp\u0026rsquo;ens fulde absolutte\nadresse, og \u003ccode\u003eref\u003c/code\u003e er den kaldende origin, hver især gennem\n\u003ccode\u003eencodeURIComponent\u003c/code\u003e. Et ukodet \u003ccode\u003e?\u003c/code\u003e eller \u003ccode\u003e\u0026amp;\u003c/code\u003e i målet ødelægger\nfortolkningen, og wallet\u0026rsquo;en åbner på sin forside i stedet for browserfanen.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eUdløsningen betyder lige så meget som linket.\u003c/strong\u003e Universelle links skifter\nkun app ved en navigation, styresystemet stoler på — og de gør med vilje\ningenting, når de indsættes i adresselinjen, hvilket også er sådan, et helt\nkorrekt link \u0026ldquo;fejler\u0026rdquo; under test.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"de-dokumenterede-formater\"\u003eDe dokumenterede formater\u003c/h2\u003e\n\n\u003cul\u003e\n\u003cli\u003ePhantom: \u003ccode\u003ehttps://phantom.app/ul/browse/\u0026lt;url\u0026gt;?ref=\u0026lt;ref\u0026gt;\u003c/code\u003e — uden \u003ccode\u003e/v1\u003c/code\u003e i\nnetop dette.\u003c/li\u003e\n\u003cli\u003eSolflare: \u003ccode\u003ehttps://solflare.com/ul/v1/browse/\u0026lt;url\u0026gt;?ref=\u0026lt;ref\u0026gt;\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eBackpack: \u003ccode\u003ehttps://backpack.app/ul/v1/browse/\u0026lt;url\u0026gt;?ref=\u0026lt;ref\u0026gt;\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eÉt mønster dækker alle tre:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003econst WALLET_BROWSE = {\n  phantom: (url, ref) =\u0026gt;\n    `https://phantom.app/ul/browse/${url}?ref=${ref}`,\n  solflare: (url, ref) =\u0026gt;\n    `https://solflare.com/ul/v1/browse/${url}?ref=${ref}`,\n  backpack: (url, ref) =\u0026gt;\n    `https://backpack.app/ul/v1/browse/${url}?ref=${ref}`,\n};\n\nfunction walletBrowseLink(\n  walletName,\n  targetUrl = window.location.href,\n) {\n  const build = WALLET_BROWSE[walletName.toLowerCase()];\n  if (!build) return null;\n  return build(\n    encodeURIComponent(targetUrl),\n    encodeURIComponent(window.location.origin),\n  );\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"udløs-linket-så-ios-og-android-accepterer-det\"\u003eUdløs linket, så iOS og Android accepterer det\u003c/h2\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eRender et rigtigt anker, beregnet på forhånd.\u003c/strong\u003e Et almindeligt\n\u003ccode\u003e\u0026lt;a href={walletBrowseLink('phantom')}\u0026gt;\u003c/code\u003e er den mest pålidelige udløser på\nbegge platforme.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSkal det ske programmatisk\u003c/strong\u003e, så tildel \u003ccode\u003ewindow.location.href\u003c/code\u003e synkront\ninde i tryk-handleren — ingen \u003ccode\u003eawait\u003c/code\u003e, ingen \u003ccode\u003efetch\u003c/code\u003e, ingen \u003ccode\u003esetTimeout\u003c/code\u003e\nførst. Efter asynkront arbejde er gestus-konteksten væk, og iOS falder\ntilbage til wallet\u0026rsquo;ens websted. Aldrig \u003ccode\u003ewindow.open\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eTest aldrig ved at indsætte i adresselinjen.\u003c/strong\u003e Universelle links udløses\nmed vilje ikke dér; test med et link, der trykkes på, eller en QR-kode, som\nkameraet scanner.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ePas på messenger-webviews.\u003c/strong\u003e Åbnet i Telegrams eller Instagrams indbyggede\nbrowser bliver universelle links ofte slugt, og wallet\u0026rsquo;ens almindelige\nwebsted indlæses i stedet. User-agent-detektion er i bedste fald et gæt, så\ngiv også brugerne en synlig nødudgang: \u0026ldquo;åbn i Safari eller Chrome, og\nforbind derefter\u0026rdquo;.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"den-større-løsning-på-android\"\u003eDen større løsning på Android\u003c/h2\u003e\n\n\u003cp\u003eHåndbyggede deeplinks er iOS-historien. På Android lader Solana Mobiles Mobile\nWallet Adapter en dApp i mobilbrowseren forbinde direkte til den installerede\nwallet-app, helt uden omvejen om den indbyggede browser. Nyere versioner af\n\u003ccode\u003e@solana/wallet-adapter-react\u003c/code\u003e registrerer mobiladapteren automatisk, så en\nopgradering af wallet-adapter-pakkerne kan løse Android alene. Målarkitekturen:\nMobile Wallet Adapter på Android, universelle browse-links på iOS, hvor Apple\nikke tillader en tilsvarende løsning.\u003c/p\u003e\n\u003ch2 id=\"efterprøv-på-en-enhed\"\u003eEfterprøv på en enhed\u003c/h2\u003e\n\n\u003col\u003e\n\u003cli\u003eRigtig enhed, wallet installeret, link åbnet fra systembrowseren — ikke fra\nen messenger.\u003c/li\u003e\n\u003cli\u003eTryk på et renderet link, eller scan en QR-kode; indsæt aldrig i\nadresselinjen.\u003c/li\u003e\n\u003cli\u003eBekræft, at wallet\u0026rsquo;en åbner, og at dApp\u0026rsquo;en indlæses i dens indbyggede\nbrowserfane — anden halvdel er den, der fejler.\u003c/li\u003e\n\u003cli\u003eGentag uden wallet\u0026rsquo;en installeret: det universelle link skal falde tilbage\ntil wallet\u0026rsquo;ens websted. Ser du dén side, mens appen er installeret, er\nlinket eller udløsningen stadig forkert.\u003c/li\u003e\n\u003cli\u003eTest derefter messenger-vejen, og tilføj \u0026ldquo;åbn i browser\u0026rdquo;-hjælpen, hvis den\nfejler dér.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"kilder\"\u003eKilder\u003c/h2\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://docs.phantom.com/phantom-deeplinks/deeplinks-ios-and-android\"\u003ePhantom: deeplinks på iOS og Android\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.solflare.com/solflare/technical/deeplinks/other-methods/browse\"\u003eSolflare: Browse-deeplinket\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.backpack.app/deeplinks/other-methods/browse\"\u003eBackpack: Browse-deeplinket\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docs.solanamobile.com/mobile-wallet-adapter/mobile-apps\"\u003eSolana Mobile: Mobile Wallet Adapter\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","date_published":"2026-08-10T00:00:00Z","date_modified":"2026-09-13T01:45:50+02:00","language":"da"}]}