<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ydelser on SuperSchnell.net</title><link>/</link><description>Recent content in Ydelser on SuperSchnell.net</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sun, 28 Dec 2025 16:32:22 +0100</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml"/><item><title>Anex - revisionsspor som kernefunktionalitet</title><link>/posts/2026-07-05-anex-revisionsspor/</link><pubDate>Sun, 05 Jul 2026 11:53:18 +0200</pubDate><guid>/posts/2026-07-05-anex-revisionsspor/</guid><description>&lt;h2 id="hvad-er-et-revisionsspor"&gt;Hvad er et revisionsspor?&lt;/h2&gt;
&lt;p&gt;Det er dybest set &amp;lsquo;Hvem gjorde hvad, hvornår?&amp;rsquo; &amp;ndash; bare for alle de data, der eksisterer i dit system. Det har en række konsekvenser; en af de vigtigste er at du er begrænset til append-only modeller. Lad mig give et eksempel, hvorfor det er vigtigt. Lad os sige, du bruger Anex til at lave en faktura til en kunde på 100.000 DKK. Der skal afregnes moms af de 100.000, der skal betales skat af det, der er tilbage, når du har betalt abonnementet til Anex, løn til dig selv osv. Lad os nu sige, kunden brokker sig og I når frem til, at de har ret &amp;ndash; det skulle have været 120.000. Du sender en ny faktura. Men, vent. Du har jo sendt momsopgørelsen til Skat for den periode. Så der skal laves en efterangivelse.&lt;/p&gt;
&lt;p&gt;Lad os sige, Anex kun havde seneste version af fakturaen? Og du bliver kaldt til Torskegilde (ja, ja &amp;ndash; jeg er så gammel, at det var den talemåde vi brugte i forrige årtusind, fordi man på et tidspunkt fik torsk serveret, når man blev inviteret ind for at kigge på gennemslagspapir&amp;hellip; Okay, det her stikker af ud af en tangent, jeg ikke havde planlagt: Tilbage på sporet!) hos de kære adminstratorer hos Skat. Held og lykke med at huske hvad der skete for to-et-halvt år siden, der gjorde at du skulle lave den efterangivelse.&lt;/p&gt;
&lt;p&gt;Man plejer at sige at Al Capone aldrig blev fanget for al volden, sprutten osv., men at det var skattevæsenet, der fældede ham. Det samme med rockerne (Det er helt utrolig svært at forklare hvordan du har købt en Harley til en halv million, når du har været på kontanthjælp de seneste femten år &amp;ndash; for ikke at tale om de tommetykke guldkæder, der omkranser din nakke).&lt;/p&gt;
&lt;h2 id="hvad-er-omfattet-af-revisionsspor"&gt;Hvad er omfattet af revisionsspor?&lt;/h2&gt;
&lt;p&gt;Alle posteringer, der vedrører økonomiske bevægelser i din virksomhed skal du kunne redegøre, hvorfra kommer. Derfor skal du altid have et bilag på dine udlæg. Derfor må der ikke være huller i dine bilagsnumre (For hvad var der på dem, du gav nummer 6,7 og 69 &amp;ndash; det her er den eneste form for humor du vil finde i denne blog). Det skal kunne spores begge veje, så når Skat finder den bilagsmappe med de dobbelt-sorte kvitteringer i (sorte kvitteringer er for sort arbejde, som Skat ikke ved noget om &amp;ndash; dobbelt-sort er dem din partner heller ikke ved noget om), så skal du kunne redegøre for, hvor de er i dit regnskabssystem. Lige så når du har sendt 5.400 EUR til din makker i Lichtenstein.&lt;/p&gt;
&lt;h2 id="hvordan-er-det-løst-i-anex"&gt;Hvordan er det løst i Anex?&lt;/h2&gt;
&lt;p&gt;Der er en klump kode, der registrerer alle ændringer i data. Den ligger centralt lige før data bliver sendt til databasen og det sker generisk, så ændringer i adressen på en kunde kan spores tilbage til hvornår det skete. Teknisk set, så er der ikke myndighedskrav om den slags, men det viser sig, at for supporten til et regnskabssystem, er det helt utroligt nyttigt at kunne sige: &amp;ldquo;Det var Henning, 7. december 2025 kl. 23:43:32, hvor han også lavede en ekstra postering til hans egen konto på 54.000 med teksten &amp;lsquo;Skurpenge&amp;rsquo;. Sjovt sammenfald &amp;ndash; var det ikke der, I havde julefrokost?&amp;rdquo;, når der er noget, der ser mærkeligt ud.&lt;/p&gt;
&lt;p&gt;Derudover kan man ikke lave ændringer til posteringer på alt, der er under en transaktion. Dvs. finansposteringer, debitorposteringer, lagerbevægelser, fakturalinjer osv.&lt;/p&gt;
&lt;h2 id="hvordan-ser-data-ud"&gt;Hvordan ser data ud?&lt;/h2&gt;
&lt;p&gt;Her er et eksempel på en ændring af stamdata:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;a676a975-89ab-4b85-b114-b47c00fafdaa&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;createdUtc&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2026-07-03T15:13:49.725Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;eventType&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;code&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;TRAILTYPE_UPDATED&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;entityType&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Partner&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;entityId&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;51&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;childType&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;childId&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;correlationId&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;361ea16d-5baf-4e4b-8c4e-50d9c50cacbb&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;oldVersion&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Address&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;addressLine1&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Mejlgade 51A&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;addressLine2&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;zipcode&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;8000&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Århus&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;country&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Updated&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2026-07-03T15:01:56.241Z&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;newVersion&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Address&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;addressLine1&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Mejlgade 51&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;addressLine2&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;zipcode&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;8000&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Århus&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;country&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Updated&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2026-07-03T15:13:28.692Z&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;unchanged&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;LabelHub ApS&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;OrgNumber&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;12345678&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;GLNNumber&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Company&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Company&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Id&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;51&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;Created&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2026-07-03T14:52:58.901Z&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Læg mærke til &lt;code&gt;correlationId&lt;/code&gt; &amp;ndash; det vil sige, at man kan søge på alt relateret, der skete da den her ændring skete. Derudover er der &lt;code&gt;childType&lt;/code&gt; og &lt;code&gt;childId&lt;/code&gt; &amp;ndash; de bruges, når fx en ordre linje ændres, så vil det også fremgå som en ændringer på den tilhørende ordre.&lt;/p&gt;
&lt;h2 id="hahaha-jeg-har-adgang-til-databasen"&gt;Hahaha, jeg har adgang til databasen&amp;hellip;&lt;/h2&gt;
&lt;p&gt;Det er fuldstændig korrekt &amp;ndash; og derfor en ansvarlig virksomhed a) betaler deres sysadms ordentligt og b) får samme sysadms til at logge adgange og begrænse dem mest muligt.&lt;/p&gt;
&lt;h2 id="og-koden"&gt;Og koden!&lt;/h2&gt;
&lt;p&gt;Se, derfor er Anex open-source, så vi kan sammenligne den kode, der kører på din server, med den der ligger på Codeberg og se de ændringer, du måtte have lavet på din version.&lt;/p&gt;</description></item><item><title>Anex - kodestil</title><link>/posts/2026-06-21-anex-kodestil/</link><pubDate>Fri, 19 Jun 2026 20:27:57 +0200</pubDate><guid>/posts/2026-06-21-anex-kodestil/</guid><description>&lt;p&gt;Jeg har en bestemt måde at kode på. Det er ikke fordi den er særlig indviklet &amp;ndash; faktisk tværtimod. I mange tilfælde vil den virke direkte banal, og det er med fuldt overlæg. En klog mand sagde på et tidspunkt, at det er ~10 gange så svært at debugge kode, som det er at skrive den &amp;ndash; hvilket betyder, at hvis jeg skrev kode lige på kanten af hvad jeg er dygtig nok til, så ville jeg ikke kunne debugge og dermed være ude af stand til at fixe bugs.&lt;/p&gt;
&lt;h2 id="ingen-exceptions"&gt;Ingen exceptions&lt;/h2&gt;
&lt;p&gt;Exceptions er i min verden forfærdelige til flow-kontrol og gør koden svær at læse. Hvis jeg kalder noget eksternt bliver det wrappet i en try-catch, der &amp;ndash; og så returneres et fejlet retur-objekt, hvis en exception sker. Der er lige præcis en undtagelse &amp;ndash; og det er en livrem og seler løsning i forbindelse med at Anex er multi-tenant (dvs. at der ligger mere end en virksomhed i databasen og på intet tidspunkt må data fra et regnskab være synligt i et andet). Hvis alle filtre og aspektorienterede koncepter fejler, så bliver der kastet en exception i det der svarer til DB-triggers i NHibernate (De kalder det event-listeners).&lt;/p&gt;
&lt;h2 id="cqrs-command-query-segregation-principle"&gt;CQRS (Command-Query Segregation Principle)&lt;/h2&gt;
&lt;p&gt;Ja, ja &amp;ndash; jeg er klar over, at inden jeg kan tælle til to, så vil der være mindst nitten CQRS-entusiaster der vil pege på alt det, der ikke er CQRS i koden. Lad os sige at CQRS er udgangspunktet. CQRS med særlige Tofty kendetegn (tm). Jeg har aldrig brudt mig om event stores (igen, ja jeg ved godt at en SQL-database teknisk set er en event store) &amp;ndash; især omkring opdatering af data, efterhånden som koden udvikler sig, har jeg ikke set nogle gode løsninger på &amp;lsquo;gamle&amp;rsquo; events.&lt;/p&gt;
&lt;p&gt;Så jeg holder mig til det jeg kender: SQL- og dokumentdatabaser og nærmer mig CQRS så godt som jeg nu kan.&lt;/p&gt;
&lt;h2 id="solid"&gt;SOLID&lt;/h2&gt;
&lt;p&gt;Hvis du ikke kender dem, så er det din næste opgave. Jeg går af og til på kompromis med S&amp;rsquo;et (Single Responsibility Principle), men jeg tror mere det er et fortolkningsspørgsmål. Ét ansvar, ja tak, men hvor bredt definerer du et ansvar? Ansvaret for databasen? Ansvaret for opdateringer? Jeg går efter den pragmatiske løsning og lader være med at lave voldsomt store klasser.&lt;/p&gt;
&lt;h2 id="tests"&gt;Tests&lt;/h2&gt;
&lt;p&gt;Terminologi er svært &amp;ndash; og ingen er enige om hvad vi kalder de forskellige niveauer af tests. Jeg fokuserer på de tests, der i min erfaring har fanget flest fejl: System- og integrationtests. Der hvor jeg har alt i spil, inklusive databaser, mine egne services, servicebussen osv. Med de værktøjer vi har til rådighed i dag med &lt;a href="https://testcontainers.com/"&gt;TestContainers&lt;/a&gt;, docker osv., så kan jeg ikke få øje på nogle undskyldninger for at undlade tests.&lt;/p&gt;
&lt;p&gt;Jeg foretrækker black-box tests fremfor white-box tests og har en tendens til at fjerne unit-tests, efter de har drevet et design.&lt;/p&gt;
&lt;h2 id="database-domænemodellen-aka-forretningslogikken"&gt;Database-/domænemodellen a.k.a. forretningslogikken&lt;/h2&gt;
&lt;p&gt;Med mindre at en parent class har brug for dens childs til validering, så vender mine dependencies altid omvendt. Dvs. at PartnerEmail (tænk: en glorificeret emailadresse) refererer Partner (tænk en juridisk person/enhed), mens Partner ikke har nogen referencer til PartnerEmail (altså, ingen collection af dem). Jeg har rigtig mange gode argumenter for det, men de er bedst illustreret ved et whiteboard og over en kop god te.&lt;/p&gt;</description></item><item><title>Anex - tekniske valg</title><link>/posts/2026-06-20-anex-tekniske-valg/</link><pubDate>Fri, 19 Jun 2026 10:47:37 +0200</pubDate><guid>/posts/2026-06-20-anex-tekniske-valg/</guid><description>&lt;p&gt;Arkitektur er et ladet begreb, som ofte kan dele vandene &amp;ndash; ikke kun for IT-systemer som Anex. Men lad os parkere midt på øretævernes holdeplads og få luftet alle beslutningerne:&lt;/p&gt;
&lt;h2 id="arkitekturen-for-anex"&gt;Arkitekturen for Anex&lt;/h2&gt;
&lt;p&gt;&lt;img src="/anex-arkitektur.png" alt="Arkitektur i Anex, vist som en række gule rektangler for de tre public services: App, Web og Api. Derudover er der fire blå datakilder &amp;ndash; en PostGres DB, et ElasticSearch cluster, en MongoDB og endelig en RabbitMQ. Til sidst er der vist tre interne lilla services: Indexing, Calculation og Subscription. Det er vist med pile, hvordan data flyder mellem de enkelte elementer. En grøn med pil i hver ende angiver at data bliver begge veje, mens en rød pil i en enkelt ende viser den modtagende part i en envejs kommunikation."&gt;&lt;/p&gt;
&lt;h2 id="argumenterne-bag"&gt;Argumenterne bag&lt;/h2&gt;
&lt;p&gt;Jeg har valgt en monolit med services og så (alt for) mange datakilder. Jeg ved godt, at monolitter er meget udskældte, men der er i min bog rigtig mange gode argumenter for en monolit i en applikation som Anex:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simplicitet &amp;ndash; stort set alle kald er direkte proces-kald. Det er &amp;rsquo;nemt&amp;rsquo; at pakke alt ind i transaktioner&lt;/li&gt;
&lt;li&gt;Enkelt at deploye&lt;/li&gt;
&lt;li&gt;Tunge kald &amp;ndash; indeksering, kalkulationer osv. kan stadig køre asynkront&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="de-mange-datakilder"&gt;De mange datakilder&lt;/h2&gt;
&lt;p&gt;Jeg skal gerne medgive, at PostGres sagtens ville kunne håndtere MongoDB&amp;rsquo;ens opgave her &amp;ndash; og det kan sagtens tænkes at jeg i en senere version flytter audit trail derind. Tanken er at der altid kun er én kilde til sandhed for en given klump data. ElasticSearch er en sekundær datakilde og kan til hver en tid gendannes ud fra de andre kilder.&lt;/p&gt;
&lt;p&gt;Hver datakilde har en primær styrke, som gør den ideel til en given opgave. PostGres er klippestabil, har fuld &lt;a href="https://en.wikipedia.org/wiki/ACID"&gt;ACID&lt;/a&gt; og understøtter adhoc queries til rapporter og den slags. MongoDB understøtter arbitrære data og er rigtig god til audit trail, hvor jeg lagrer snapshots af data før og efter en rettelse &amp;ndash; det -kan- PostGres også, men det er noget jeg lige skal have læst op på, for at finde en god model. ElasticSearch er virkelig god til fritekstsøgning inkl. søgning i filer (via &lt;a href="https://github.com/tomlm/Tiki.Net"&gt;Tiki.Net&lt;/a&gt;). Derudover har de alle åben kildekode, hvilket var et absolut krav.&lt;/p&gt;
&lt;h2 id="sprog-og-frameworks"&gt;Sprog og frameworks&lt;/h2&gt;
&lt;p&gt;Alt pånær Anex.Web er skrevet i .NET C# &amp;ndash; jeg er 100p klar over at det ikke er et åbenlyst valg, når vi nu kører open source, men det er nu engang det sprog, jeg er bedst til. Til Anex.Web har jeg valgt &lt;a href="https://svelte.dev/"&gt;Svelte&lt;/a&gt;, som det mindst dårlige valg i et hav af dårlige valg. De vigtigste frameworks i backend koden er &lt;a href="https://nhibernate.info/"&gt;NHibernate&lt;/a&gt;, &lt;a href="https://github.com/rebus-org"&gt;RebusFM&lt;/a&gt;, &lt;a href="https://fluentmigrator.github.io/"&gt;FluentMigrate&lt;/a&gt; &amp;ndash; og så &lt;a href="https://docs.avaloniaui.net"&gt;Avalonia&lt;/a&gt; til Xena.App.&lt;/p&gt;
&lt;p&gt;Det er ikke de mest kendte frameworks, men til gengæld dem, jeg har haft bedst erfaringer med til det jeg skal bruge. De udmærker sig ved at være &amp;lsquo;gamle&amp;rsquo; (læs: battle-hardened) og forholdsvis simple set op mod funktionalitet.&lt;/p&gt;</description></item><item><title>Anex - ERP System</title><link>/posts/2026-06-19-anex-erp-system/</link><pubDate>Fri, 19 Jun 2026 09:53:20 +0200</pubDate><guid>/posts/2026-06-19-anex-erp-system/</guid><description>&lt;p&gt;De sidste par år har jeg arbejdet på et projekt, når der har været tid og lyst &amp;ndash; det er nu så stort, at det er på tide at begynde at snakke om det. Dels er jeg nået til et punkt, hvor jeg har en ret god ide om, hvad jeg vil opnå og dels er nogle af de tilbageværende features noget, jeg ikke er dygtig (nok) til.&lt;/p&gt;
&lt;h2 id="historien-bag-anex-tldr"&gt;Historien bag Anex, TL;DR&lt;/h2&gt;
&lt;p&gt;Fra ~2010-2019 var jeg med til at bygge &lt;a href="https://xena.biz"&gt;Xena&lt;/a&gt; &amp;ndash; i 2017 blev det købt af &lt;a href="https://eg.dk"&gt;EG&lt;/a&gt; og jeg var ikke enig i projektets retning derefter. Jeg er stadig stolt af det, vi fik bygget &amp;ndash; og navnet Anex skal der nok ikke en Sherlock Holmes til at gætte hvor stammer fra.&lt;/p&gt;
&lt;p&gt;Anex startede for at genopfriske mange af de koncepter, vi brugte dengang &amp;ndash; og samtidig forsøge at finde løsninger på de ting, der skurrede. Over tid er projektet så blevet mere aktuelt &amp;ndash; dels gik jeg selvstændig og dels er der kommet en ny bogføringslov, som &amp;rsquo;tvinger&amp;rsquo; selv små selvstændige til at skulle anskaffe et online bogføringssystem.&lt;/p&gt;
&lt;h2 id="hvorfor-genbygge-dinero-billy-osv"&gt;Hvorfor genbygge Dinero, Billy osv.&lt;/h2&gt;
&lt;p&gt;Tjah. Jeg har rigtig mange forskellige vinkler og svar på det &amp;ndash; men ingen af dem trumfer: &amp;lsquo;Fordi jeg kan og har lyst&amp;rsquo;. Det er selvfølgelig samtidigt et fattigt svar for alle andre end mig, så jeg skal gerne uddybe mine ideer, og så står det jo alle frit for, at se om der er noget værdifuldt gemt midt i Odense.&lt;/p&gt;
&lt;h2 id="hvorfor-open-source"&gt;Hvorfor open-source?&lt;/h2&gt;
&lt;p&gt;Fordi det er det bedste, du kan gøre for et projekt, du gerne vil have til at eksistere på den lange bane. Det var faktisk en af de ting, vi gerne ville dengang med Xena, men hvor økonomiske interesser kom i vejen.&lt;/p&gt;
&lt;h2 id="hvorfor-ikke-bare-bidrage-til-et-af-de-andre-opensource-systemer"&gt;Hvorfor ikke bare bidrage til et af de andre opensource systemer?&lt;/h2&gt;
&lt;p&gt;Fordi de ikke vil de samme ting, som jeg vil.&lt;/p&gt;
&lt;h2 id="hvor-er-koden-hva-hva"&gt;Hvor er koden, hva&amp;rsquo;, hva&amp;rsquo;?&lt;/h2&gt;
&lt;p&gt;Den er på &lt;a href="https://codeberg.org"&gt;Codeberg&lt;/a&gt; i et privat repository. Hvis du gerne vil kigge, så kontakt mig på mail med dit brugernavn på Codeberg, HVIS du højhelligt lover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ikke at fodre koden til at GenAI (det inkluderer at lave en public fork på Github/Gitlab fx)&lt;/li&gt;
&lt;li&gt;Ikke bruge GenAI til at danne kode i dine PR&amp;rsquo;s&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Koden er under en MIT licens, så der er frit slag til at modificere, udgive osv.&lt;/p&gt;
&lt;h3 id="hvad-nu-hvis-jeg-gør-det-alligevel"&gt;Hvad nu, hvis jeg gør det alligevel?&lt;/h3&gt;
&lt;p&gt;Så bliver jeg sur og rejser en skamstøtte her på siden.&lt;/p&gt;
&lt;h2 id="hvad-så-nu"&gt;Hvad så nu?&lt;/h2&gt;
&lt;p&gt;Det her var bare en teaser &amp;ndash; jeg følger op med en masse indlæg om de tanker, der ligger bag koden og projektet.&lt;/p&gt;</description></item><item><title>Estimering og planlægning episode 3</title><link>/posts/2026-03-18-estimering-og-planlaegning-episode-tre/</link><pubDate>Wed, 18 Mar 2026 17:05:04 +0100</pubDate><guid>/posts/2026-03-18-estimering-og-planlaegning-episode-tre/</guid><description>&lt;h4 id="serien"&gt;Serien&lt;/h4&gt;
&lt;p&gt;&lt;a href="/posts/2026-01-02-estimering-og-planlaegning-episode-et"&gt;Episode 1&lt;/a&gt; &amp;ndash; Den med forudsætningerne&lt;br&gt;
&lt;a href="/posts/2026-01-03-estimering-og-planlaegning-episode-to"&gt;Episode 2&lt;/a&gt; &amp;ndash; Den med opsplitningen&lt;br&gt;
Episode 3 &amp;ndash; Den med den grove nedbrydning (du er her)&lt;/p&gt;
&lt;p&gt;Strategisk estimering. Vores projekt har nu rammerne, vi har en prioriteret liste af opgaver &amp;ndash; hvor forretningen har valgt hvilke funktionaliteter, der giver mest forretningsmæssig værdi. Nu skal det kobles med hvad hver del koster at lave. En dygtig udvikler har prøvet lidt af hvert &amp;ndash; de ved hvor diverse dyr er begravet rundt omkring i systemlandskabet, og de bør have en god fornemmelse af teamet. Læg mærke til at jeg intet skrev om lead-/senior- og andre titler for udvikleren. Ofte er det faktisk en fordel, at have en blanding af erfarne udviklere og forholdsvis nye udviklere med i den her proces. En virkelig dårlig ide er at samle alle de største egoer i et lokale, hvor der skal gives forholdsvis hurtige estimater.&lt;/p&gt;
&lt;h2 id="kompleksitet"&gt;Kompleksitet&lt;/h2&gt;
&lt;p&gt;Vi skal bruge estimater, der siger noget om a) kompleksiteten i den enkelte opgave og b) usikkerheden. Nogen bruger T-shirt størrelser (tænk Large, Medium og Small) andre bruger udvikleruger &amp;ndash; detaljerne er ligegyldige, tidsforbruget er ligegyldigt (på nuværende tidspunkt). De estimateter vi laver her, har en række andre formål &amp;ndash; de er relative til hinanden. Det første og vigtigste formål er at danne et overblik over om det overhovedet er et godt projekt. Hvis alt er mere komplekst og tungt end forventet, så er en valid beslutning her at nedlægge projektet. Alternativt kan projektet skæres til &amp;ndash; det skal virke sandsynligt at de tilbageværende opgaver kan løses inden for tidsrammen.&lt;/p&gt;
&lt;p&gt;Næste formål med den her grovestimering er at finde ud af om udviklergruppen indeholder alle de kompetencer vi skal bruge &amp;ndash; eller om vi skal bruge nogle ekstra resourcer. Dernæst bør den her estimering resultere i at prioriteringen af opgaverne genovervejes. En afvejning mellem hvor dyr (målt i kompleksitet) den enkelte funktionalitet er i forhold til forretningsværdien. Igen, jo hurtigere vi kan vise fremdrift i et projekt &amp;ndash; jo større engagement og positivitet vil der være om projektet. Hvis vi med kun en moderat indsats kan levere værdifuld funktionalitet i uge 3 af projektet? Så har vi fået en flyvende start.&lt;/p&gt;
&lt;h2 id="hvad-der-sker-her-bliver-her"&gt;Hvad der sker her, bliver her&lt;/h2&gt;
&lt;p&gt;En absolut dødssynd er at bruge de her estimater til senere at følge op på om udviklergruppen så nåede den enkelte feature på den tid, de &amp;rsquo;lovede&amp;rsquo;. Det er gift for næste projekt, fordi den enkelte udvikler så vil begynde at gange alle estimater med to. Eller tre. Al erfaring siger, at vi gætter vildt forkert i det her stadie &amp;ndash; jo flere features, jo mere forkert gætter vi. Til gengæld gætter vi forkert i begge retninger, pointen er, at det her er en del af den lærende proces. Jo flere projekter vi laver, jo bedre en fornemmelse vil vi få for de hurdler, der er i organisationen.&lt;/p&gt;
&lt;p&gt;Det vigtige her er at det er intuitionsbaserede estimater (også kendt som WAG&amp;rsquo;s &amp;ndash; wild-ass guesses) &amp;ndash; ingen detaljer er tilladt. Hvis det hjælper, så brug et spænd til at beskrive kompleksiteten. 2-12 udvikleruger er et validt gæt &amp;ndash; det signalerer også at det er en opgave, vi skal være særligt opmærksom på, når vi kommer tættere på udførelsen. Hvis spændet er 20-250? Så skal opgaven brydes mere ned; den er for uhåndterlig og for usikker til at en god projektleder vil røre den med en ildtang.. i en asbestdragt.. på fyrre meters afstand..&lt;/p&gt;
&lt;h2 id="konklusion"&gt;Konklusion&lt;/h2&gt;
&lt;p&gt;Denne her indledende fægteduel mellem udviklingen og forretningen er i høj grad en forventningsafstemning. Er det business-as-usual, nu i rød&amp;hellip; eller er det alle-mand-til-pumperne-det-kommer-til-at-gøre-ondt, vi taler om. Den forståelse mellem afdelingerne er guld værd &amp;ndash; ikke bare til det her projekt men til den endeløse række, der ligger i fremtiden.&lt;/p&gt;</description></item><item><title>Estimering og planlægning episode 2</title><link>/posts/2026-01-03-estimering-og-planlaegning-episode-to/</link><pubDate>Sat, 03 Jan 2026 08:48:30 +0100</pubDate><guid>/posts/2026-01-03-estimering-og-planlaegning-episode-to/</guid><description>&lt;h4 id="serien"&gt;Serien&lt;/h4&gt;
&lt;p&gt;&lt;a href="/posts/2026-01-02-estimering-og-planlaegning-episode-et"&gt;Episode 1&lt;/a&gt; &amp;ndash; Den med forudsætningerne&lt;br&gt;
Episode 2 &amp;ndash; Den med opsplitningen (du er her)&lt;br&gt;
&lt;a href="/posts/2026-03-18-estimering-og-planlaegning-episode-tre"&gt;Episode 3&lt;/a&gt; &amp;ndash; Den med den grove nedbrydning&lt;/p&gt;
&lt;p&gt;Lad os nu sige, at alle de perfekte forhold er tilstede for projektet &amp;ndash; ledelsen er med, resourcerne er tilstede og der er et solidt forankret mål for det hele (Alt det i episode 1). Nu er det tid at få projektet splittet op. Igen, projektmodellen er ikke rigtig vigtig på det her stadie &amp;ndash; det er mere et spørgsmål om at danne rammerne.&lt;/p&gt;
&lt;h2 id="detaljer-er-fjenden"&gt;Detaljer er fjenden&lt;/h2&gt;
&lt;p&gt;På nuværende tidspunkt er vi interesseret i &lt;strong&gt;hvad&lt;/strong&gt; der skal bygges, ikke hvordan. Hvilket web framework, der skal anvendes; om det skal være en microservice arkitektur; hvor det skal hostes. Alt sammen i bedste fald distraktion på nuværende tidspunkt. Nej, vi skal have klarlagt præcis hvilke forretningsbehov projektet skal løse &amp;ndash; og, mindst ligeså vigtigt, hvilke procedurer vil vi gerne ændre. Der skal være noget &amp;lsquo;godt&amp;rsquo; for enden af regnbuen, der skal være noget mærkbart, der er &amp;lsquo;bedre&amp;rsquo; i forretningen bagefter. Hvis det er et kundevendt projekt, skal der være &amp;rsquo;noget for dem&amp;rsquo;. Det virker banalt at skrive det, men det går overraskende ofte galt. Enten glemmer man de ændringer, der skulle til i organisationen for at understøtte det nye software &amp;ndash; eller man glemmer at spørge brugerne, hvad de egentlig har brug for.&lt;/p&gt;
&lt;h2 id="prioritering-prioritering-prioritering"&gt;Prioritering, prioritering, prioritering&lt;/h2&gt;
&lt;p&gt;Efterhånden som projektet bliver nedbrudt i hvilke arbejdsgange, processer og organisationsændringer, der er påvirket &amp;ndash; så skal det prioriteres, hårdt. Der kan kun være en prioritet 1. &amp;ldquo;Jamen, det hele er vigtigt!&amp;rdquo; (jeg kan høre præcis &lt;em&gt;den&lt;/em&gt; indkøbschef for mit indre øre) Og ja, det hele er vigtigt, ellers ville det ikke være med i projektet &amp;ndash; vi beskæftiger os ikke med ikke-vigtige ting. Men. Det er ikke &lt;strong&gt;lige&lt;/strong&gt; vigtigt. Tænk det som &amp;ldquo;Hvis det her ikke er der, er projektet dødfødt&amp;rdquo;-vigtighedsgrader. Altså, design projektet efter, at hvis vi når de 80% højst prioriterede ændringer, så er det en success (læs: det giver værdi for forretningen). 80-150% er bonus. (Og ja, jeg har været med i projekter, hvor vi nåede alle prioriteterne og fik et par boblere med)&lt;/p&gt;
&lt;h2 id="rækkefølgen--byg-det-vigtigste-først"&gt;Rækkefølgen &amp;ndash; Byg det vigtigste først&lt;/h2&gt;
&lt;p&gt;Med mindre. Der vil altid være afhængigheder mellem de prioriterede opgaver &amp;ndash; og så er der den eviggyldige sandhed: Det psykologiske betyder 71,9% [1] af succes-raten for projekter. Et feedback-loop så tidligt som muligt bør prioriteres. Det har mindst to formål: Tidlig identificering af logiske fejlslutninger og følelsen af fremskridt. Igen, stadig uafhængigt af om vi kører med vandfald eller agilt. Men ellers er hovedreglen &amp;ndash; det vigtigste først &amp;ndash; og byg hver del &lt;strong&gt;færdig&lt;/strong&gt; [2]. Og ja, det inkluderer ting som tilgængelighed og lokalisering. Det er a) svært og b) tidskrævende at bygge ind som en eftertanke. I agile kredse kalder man det DoD (Definition of Done) &amp;ndash; hvornår kan man betragte dette område for færdigt. Testere kalder det Acceptance Tests. Der skal være 100p enighed om det fra alle, der har bacon på linjen.&lt;/p&gt;
&lt;h2 id="konklusion"&gt;Konklusion&lt;/h2&gt;
&lt;p&gt;Den her fase af projektet er super-kritisk. Den skal få alle, der er med i projektet på samme side i manuskriptet i forhold til &lt;strong&gt;Hvorfor&lt;/strong&gt;. Hvor passer det her ind i forretningen; hvem hjælper vi; hvad ændrer vi &amp;ndash; find selv på flere hv-ord. Alt for ofte har jeg set diskussioner, der er kørt ud af tangenter om hvorvidt det ene framework var bedre end et andet.&lt;/p&gt;
&lt;p&gt;[1] Bestemt ud fra upartisk terningerul af min datter.&lt;br&gt;
[2] Ikke færdig-færdig &amp;ndash; funktion over form (læs: Vent med polering)&lt;/p&gt;</description></item><item><title>Estimering og planlægning, episode 1</title><link>/posts/2026-01-02-estimering-og-planlaegning-episode-et/</link><pubDate>Fri, 02 Jan 2026 05:58:18 +0100</pubDate><guid>/posts/2026-01-02-estimering-og-planlaegning-episode-et/</guid><description>&lt;h4 id="serien"&gt;Serien&lt;/h4&gt;
&lt;p&gt;Episode 1 &amp;ndash; Den med forudsætningerne (du er her)&lt;br&gt;
&lt;a href="/posts/2026-01-03-estimering-og-planlaegning-episode-to"&gt;Episode 2&lt;/a&gt; &amp;ndash; Den med opsplitningen&lt;br&gt;
&lt;a href="/posts/2026-03-18-estimering-og-planlaegning-episode-tre"&gt;Episode 3&lt;/a&gt; &amp;ndash; Den med den grove nedbrydning&lt;/p&gt;
&lt;p&gt;Der er skrevet tykke bøger om, hvordan software bør planlægges og bygges. Og der er kurser. Og uddannelser med tilhørende certificeringer. Og konsulenter. Alligevel går det stort set altid galt (i en eller anden grad) af mange forskellige årsager. Jeg tror efterhånden, jeg har prøvet alle modellerne &amp;ndash; fra &amp;lsquo;Plan, pssh &amp;ndash; vi ved jo godt hvad vi skal lave&amp;rsquo; over diverse agile modeller til Vandfald TTM (I nogle spektakulære tilfælde var det at lave projektplanen i sig selv et projekt, der krævede sin egen model&amp;hellip;) og min erfaring er at den valgte model ikke har haft afgørende indflydelse på om projektet er lykkedes eller ej. Det her indlæg bliver en del af en længere serie, for jeg har holdninger (tm).&lt;/p&gt;
&lt;h2 id="risikofaktorerne"&gt;Risikofaktorerne&lt;/h2&gt;
&lt;p&gt;Størrelse &amp;ndash; dræberen af alle projekt(plan)er. Der er masser af andre risici, men de blegner alle i forhold til størrelsen af projektet. Kompleksiteten og usikkerheden af et hvilket som helst projekt stiger ikke linært med størrelsen af projektet &amp;ndash; de stiger eksponentielt. Forskellen mellem at skulle bygge software til at løse en specifik lille del af forretningen og så at skulle bygge software som skal håndtere 100 små sspecifikke dele af forretningen, som har indbyrdes afhængigheder&amp;hellip; Bare at regne ud hvilke dele, der skal bygges før andre, kan hurtigt blive et projekt i projektet.&lt;/p&gt;
&lt;p&gt;Det grundlæggende problem er egentlig ikke det at bygge software &amp;ndash; som enhver udvikler vil fortælle dig, så er det ligeså nemt at følge en kravspecifikation som det er at gå på vandet &amp;ndash; hvis de bare er frosset, skal det nok gå. Problemet er at software skal løse et forretningsproblem i virkeligheden &amp;ndash; og virkeligheden er kompleks og ændrer sig over tid.&lt;/p&gt;
&lt;p&gt;Så er der ledelsen af projektet og her mener jeg ikke projektlederen, men de personer, der har besluttet at projektet skal startes. Hvis der ikke er defineret et klart mål og en benhård prioritering fra start, kommer det til at være en klods om benet i hele projektets levetid.&lt;/p&gt;
&lt;p&gt;Den sidste store risikofaktor er organiseringen af projektgruppen. Alle projekter har brug for involvering af nøglepersoner i processen &amp;ndash; ligegyldigt om de er en fast del af projektgruppen eller der er afsat tid til at de kan agere konsulenter. Der er en række formål med det &amp;ndash; dels at de får ejerskab, dels at der sikres mod misforståelser (læs: kvalitetssikring). Hvis et projekt ikke er solidt forankret, er der stor risiko for at slutproduktet aldrig opnår det potentiale, der var hele grunden til udførelsen.&lt;/p&gt;
&lt;h2 id="det-er-altid-chefens-skyld"&gt;Det er altid chefens skyld&lt;/h2&gt;
&lt;p&gt;Provokerende, men ikke desto mindre rigtigt. Chefers fornemste opgave er at sikre at deres medarbejdere lykkes med deres opgaver, herunder projekter &amp;ndash; som en umiddelbar konsekvens: De skal skabe rammerne, som muliggør succes. Igen, vi taler ikke om projektledelsen &amp;ndash; vi snakker om den &amp;lsquo;rigtige&amp;rsquo; ledelse. Ligegyldig hvor god en udviklergruppe og projektledelse, du har &amp;ndash; hvis ledelsen ikke er a) direkte involveret og b) bakker 100p op om projektet så er projektet virkelig udfordret. Jeg siger ikke, det er umuligt &amp;ndash; jeg siger blot at det er ikke er lykkes endnu at opdrætte fisk i sand.&lt;/p&gt;
&lt;h2 id="konklusion"&gt;Konklusion&lt;/h2&gt;
&lt;p&gt;Selv hvis man gør alt det rigtige &amp;ndash; små projekter, massiv ledelsesinvolvering og allokering af tilstrækkelige resourcer, er det ikke garanti for succes, men! der er en selvstændig pointe i, at selv hvis vi fejler &amp;ndash; så er vi blevet klogere på forretningen og tabet af resourcer er ikke truende for forretningens eksistens. Jeg er abonnement på ideen om den lærende organisation og hvis vi gør det legitimt at fejle, så lærer vi hurtigere og er bedre rustet til næste projekt. For en ting er sikkert, den dag en forretning stopper med projekter er samtidig dagen, hvor der kun er afvikling tilbage.&lt;/p&gt;
&lt;p&gt;Valg af projektmodel er en spændende nok diskussion, men hvis ovenstående ikke er på plads, er det som at flytte på skraldespandene i en brændende bygning &amp;ndash; en god workout, men samtidig skønne, spildte kræfter.&lt;/p&gt;</description></item><item><title>GenAI og andre vederstyggeligheder</title><link>/posts/2025-12-29-genai-og-andre-vederstyggeligheder/</link><pubDate>Mon, 29 Dec 2025 07:02:45 +0100</pubDate><guid>/posts/2025-12-29-genai-og-andre-vederstyggeligheder/</guid><description>&lt;p&gt;Jeg kan med nogen stolthed sige, at jeg ikke har brugt ChatGPT, Dall-E og andre (de)generative AI-modeller &amp;ndash; og kommer ikke til det. I min verden er der to typer af problemer med teknologien: De etisk/moralske og så de tekniske.&lt;/p&gt;
&lt;h2 id="etik-og-moral"&gt;Etik og moral&lt;/h2&gt;
&lt;p&gt;De firmaer som står bag de her værktøjer respekterer ingen regler og opfører sig som græshopper i deres uendelige søgen efter mere materiale &amp;ndash; og de ved det godt selv. Mange open-source projekter oplever at 95+% af deres trafik kommer fra scrapere. De omgår alle de gængse bot-værn (robots.txt osv.) &amp;ndash; senest via browser(plugins), så når en vilkårlig bruger lander på en hjemmeside, sender den lige en kopi af det viste til gæt-en-anløben-type.&lt;/p&gt;
&lt;p&gt;Licenser, ophavsret og andre intellektuelle rettigheder bliver rutinemæssigt ignoreret. Med et økonomisk begreb eksternaliserer de omkostningerne til organisationer og privatpersoner, der aldrig har accepteret vilkårene og ej heller får del i de &amp;lsquo;værdier&amp;rsquo;, der skabes.&lt;/p&gt;
&lt;p&gt;Så er der resourceforbruget i datacentrene. Det er voldsomme mængder af strøm, vand og hardware, der bliver kylet efter de her modeller &amp;ndash; at det på ingen måde stemmer overens med den (ret begrænsede) nytte, der kommer ud af dem. Vi snakker mængder, hvor de seriøst overvejer at bruge jetmotorer til at lave nok strøm og få naboer til at begrænse deres bade.&lt;/p&gt;
&lt;h2 id="teknikken"&gt;Teknikken&lt;/h2&gt;
&lt;p&gt;Modellerne er gigantiske statistiske modeller &amp;ndash; meget forsimpelt teknikken du oplever, når du skriver en SMS og telefonen foreslår næste ord. Ideen er, at tage så mange skrevne kilder som muligt, splitte dem ned i mindste bestanddele og så &amp;lsquo;forudsige&amp;rsquo; et svar ved at tage de mest sandsynlige ord ud fra en lang række parametre. Der er ingen smålig skelen til om kildeteksten kommer fra Reddit, Hestenettet eller et tilfældigt indlæg på en kodesnedkers blog udover &amp;lsquo;vægte&amp;rsquo; &amp;ndash; mao. så vægter en shitpost på Reddit langt højere end et indlæg på en tilfældig blog.&lt;/p&gt;
&lt;p&gt;Vigtigere, er der ingen skelnen i forhold til korrekthed. Dvs. at hvis man bruger kodeværktøjer baseret på GenAI, så vil de foreslå kode, der a) ikke har korrekt syntax, b) er uddateret og/eller c) ikke passer med den eksisterende kode &amp;ndash; og der vil blive foreslået kode, som er overrepræsenteret i kildedata.&lt;/p&gt;
&lt;p&gt;Den sidste pointe kræver en forklaring. Jeg har arbejdet med kode i omkring 25 år. Jeg har lært rigtig meget i løbet af den tid &amp;ndash; min bedste kode er den jeg laver i dag. Når jeg kigger på min egen kode, der er mere end tre måneder gammel&amp;hellip; så er der ikke plads i mine sko til mine tæer. Så hvis vi tager mine samlede værker, så er 99% af det i kategorier mellem &amp;lsquo;ringe&amp;rsquo; og &amp;lsquo;decideret forkerte&amp;rsquo; (25 år = 100 kvartaler, kun det sidste kvartal vil jeg vise frem med stolthed). Og hvis der er noget, internettet har bevist over tid, så er det hvor meget alle mennesker tager fejl langt det meste af tiden.&lt;/p&gt;
&lt;p&gt;Sidst, men bestemt ikke mindst &amp;ndash; så er jeg system-menneske. Jeg vægter forudsigelighed og stabilitet langt højere end hastighed. ChatGPT er baseret på et interface, hvor man skal gætte på syntax. Over tid bliver man så bedre til at gætte ud fra arbitrære &lt;del&gt;myter&lt;/del&gt; regler, som skifter over tid (når der kommer nye versioner af systemet). Selv hvis jeg gætter rigtigt, vil jeg ikke få samme resultat som sidst, hvilket totalt diskvalificerer det som et værktøj for mig.&lt;/p&gt;
&lt;h2 id="konklusion"&gt;Konklusion&lt;/h2&gt;
&lt;p&gt;Ovenstående er kun et udsnit af problemerne ved GenAI &amp;ndash; derudover er der de kognitive påvirkninger. Når Microsoft selv har forsket sig frem til, at udviklerne a) overvurderer gevinsten (som tenderer til at være negativ) og b) bliver dårligere over tid, så bliver det et nej-tak, herfra.&lt;/p&gt;
&lt;p&gt;Der findes helt sikkert use-cases for dem &amp;ndash; som en marginalt bedre Google Translate, når du står i et fremmed land. Eller som fallback, når udviklerne for n&amp;rsquo;te gang ikke har indtænkt tilgængelighed i deres brugerflader. Jeg synes bare slet, slet ikke det står mål med omkostningerne &amp;ndash; både i forhold til klima, sårbare menneskers afhængighed og presset på open source udviklere, forfattere og kunstnere. De resourcer kunne vi med fordel have anvendt til at &lt;em&gt;løse&lt;/em&gt; de problemer, istedet.&lt;/p&gt;</description></item><item><title>En kodesnedkers bekendelser</title><link>/posts/2025-12-29-en-kodesnedkers-bekendelser/</link><pubDate>Sun, 28 Dec 2025 16:40:10 +0100</pubDate><guid>/posts/2025-12-29-en-kodesnedkers-bekendelser/</guid><description>&lt;p&gt;Kodning er for mig noget, der kom snigende. Tilbage i forrige årtusind var jeg knapt klar over, at formler i Lotus 1-2-3 var kodning. Det var bare en genvej til ikke at skulle opdatere totaler via viskelæder og mere grafit.&lt;/p&gt;
&lt;p&gt;Da jeg byggede et system i Access til at lave overslag på køleanlæg, var det fordi vores Excelark var i atten versioner og der var vidt forskellige priser i dem. Et givent tilbuds totale pris afhang af, hvilket tidligere tilbud sælgeren tog udgangspunkt i. Til min gru bliver det system stadig brugt.&lt;/p&gt;
&lt;p&gt;Så var der økonomisystemerne, hvor man kunne tilpasse logikken, så manuelle processer kunne strømlines og effektiviseres. Det foregik i horrible afarter af BASIC-lignende syntax og bygget ovenpå databaser, der var glorificerede tekstfiler med indeks i andre tekstfiler.&lt;/p&gt;
&lt;p&gt;Det var først, da jeg blev hyret til at bygge &lt;a href="https://xena.biz"&gt;Xena&lt;/a&gt; i 2010, at jeg reelt begyndte at kalde mig programmør og systemudvikler. Dengang var det helt almindeligt ikke at have en formel uddannelse indenfor faget &amp;ndash; vi var kodesnedkere, praktikere.&lt;/p&gt;
&lt;p&gt;Jovist, vi var nogle stykker, der tog det alvorligt at læse op på alt det, vi ikke vidste &amp;ndash; design patterns, optimering af SQL osv. &amp;ndash; men vi var et fåtal. Fokus var at få noget ud i verden, der løste et problem eller optimerede en proces. Vedligehold var i bedste fald en eftertanke.&lt;/p&gt;
&lt;p&gt;Mange ord for at forklare formålet med bloggen her &amp;ndash; al min erfaring omkring spændingsfeltet mellem forretning og IT &amp;ndash; og så alle mine hot-takes. Der er masser af mere tekniske blogs derude af folk, der er mig overlegne. Folk som &lt;a href="https://ayende.com"&gt;Ayende&lt;/a&gt; der lærte mig rigtig meget om &lt;a href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping"&gt;ORM&lt;/a&gt; og mere specifikt &lt;a href="https://nhibernate.info/"&gt;NHibernate&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Metode</title><link>/method/</link><pubDate>Sun, 28 Dec 2025 16:32:22 +0100</pubDate><guid>/method/</guid><description>&lt;h2 id="udgangspunktet"&gt;Udgangspunktet&lt;/h2&gt;
&lt;p&gt;Udvikling og forløb aftales altid i samarbejde med jer som kunde. Nedenstående er alene tænkt som inspiration fra tidligere erfaringer med projekter.&lt;/p&gt;
&lt;h2 id="opgraderinger-af-legacy-kode"&gt;Opgraderinger af legacy kode&lt;/h2&gt;
&lt;p&gt;Det optimale i forbindelse med en opgradering af en kodebase til en nyere version, er at der ikke samtidig sker ændringer i den gamle løsning. Det er dog ikke altid muligt i forhold til kundekrav, bugfixes mv. Det er min erfaring, at den bedste løsning i den forbindelse er at lade opdateringen ske isoleret fra legacy koden - og at eventuelle ændringer trækkes ind løbende, typisk hver uge.&lt;/p&gt;
&lt;p&gt;Herved opnås:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Udviklingen ligger ikke &amp;lsquo;stille&amp;rsquo;&lt;/li&gt;
&lt;li&gt;Den gamle og den nye løsning kan køres samtidig, så det kan kontrolleres, at den er implementeret korrekt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Først når løsningen er konverteret 1:1, laves der refaktoreringer og ændringer, så kunder ikke oplever nogen ændringer i skiftet fra den gamle til den nye version - og der er ikke tvivl om, hvornår en eventuel uhensigtsmæssighed er opstået.&lt;/p&gt;
&lt;h2 id="undervisning"&gt;Undervisning&lt;/h2&gt;
&lt;p&gt;For at give så stort et udbytte som muligt, tager vi et indledende møde med beslutningstager og en eller to fra det team, der skal undervises. Formålet er at jeg kan danne mig et indtryk af niveauet og hvilke områder, der skal lægges særlig vægt på. Herefter laver jeg materiale og eventuelle praktiske øvelser, som efter kursets afholdelse udleveres som kildemateriale til fri afbenyttelse.&lt;/p&gt;</description></item><item><title>Om mig</title><link>/about/</link><pubDate>Sun, 28 Dec 2025 16:30:05 +0100</pubDate><guid>/about/</guid><description>&lt;h2 id="baggrund-i-regnskabsverdenen"&gt;Baggrund i regnskabsverdenen&lt;/h2&gt;
&lt;p&gt;Oprindeligt uddannet som revisorassistent hos Deloitte i 1998 kombineret med en HD-O i Organisation og Ledelse med specialet &amp;ldquo;Indkøb og sourcing&amp;rdquo;. Efterfølgende konverteret en række ældre økonomisystemer til (dengang) moderne systemer, hvilket har givet mig erfaring med hvordan organisationer fungerer, og hvordan forretningen kan optimeres ved brug af data.&lt;/p&gt;
&lt;h2 id="tidligere-projekter"&gt;Tidligere projekter&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;2025- Aarhus Universitet&lt;/strong&gt; Udvikling af EHub, en integrationsplatform til at udstille data fra eksterne systemer til brug for interne processer, herunder BI og ledelsesinformation. Forventes i drift start 2027.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C# MSSQL EFCore Azure ServiceBus OracleCampusSystems REST&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;2025 LabelHub ApS&lt;/strong&gt; Sparring og teknisk løsning af problemstillinger ved funktionsudvidelse og reducering af teknisk gæld til cloud-løsning til håndtering af etiketfremstilling. Udvidelse og opgradering af eksisterende funktionalitet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C# MySQL NHibernate AWS SQS SNS Lambda ElasticSearch REST&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;2023-2025 Solita A/S&lt;/strong&gt; Tre projekter med opgraderinger fra .NET classic til .NET 8.0 - et større ERP-system til håndværkere (~550 timer ialt), et mindre system til registrering af skibslast (~70 timer) og endelig planlægningen af opgraderingen for et system til de danske Regioner (estimeret til ~1500 timer).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C# MSSQL MySQL PostGres EFCore REST&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;2021-2023 team.blue&lt;/strong&gt; Udbygning af interne integrationer til Microsoft, iubenda og HubSpot til synkronisering af data i en microservices arkitektur driftet i Kubernetes på intern serverpark.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C# MSSQL EFCore Solr Docker Kubernetes REST&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;2020 QuickOrder ApS&lt;/strong&gt; Udbygning af løsning, der håndterer menu, Point-of-Sale og løn til restauranter med særlig fokus på integration til økonomisystemerne: Xena, Dinero, e-conomic, Billy og Visma.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C# MySQL AWS SNS Lambda CloudFormation REST&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;2010-2019 Xena Ap&lt;/strong&gt;S Jeg var lead udvikler på regnskabssystemet Xena, som nu fungerer som økonomisystemet i EG&amp;rsquo;s brancheløsning for enterprise. Jeg var med fra 2010 - 2019 og stod for udviklingen af backend og databaseintegration samt omdannelse fra forretnings- og kundekrav til implementeret funktionalitet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C# MSSQL RabbitMQ NHibernate ElasticSearch MongoDB REST&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="undervisningserfaring"&gt;Undervisningserfaring&lt;/h2&gt;
&lt;p&gt;Som tidligere holdleder i uddannelsesenheden i Beredskabskorpset har jeg hvad der svarer til voksenpædagogisk grundkursus, og har tidligere afholdt kurser i WPF, NHibernate og Entity Framework Core, både til interne og eksterne kursister.&lt;/p&gt;
&lt;h2 id="tidligere-arbejdsgivere"&gt;Tidligere arbejdsgivere&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;2023-25 Solita - senior udvikler&lt;/strong&gt;&lt;br&gt;
Konsulent på kundeprojekter hos private og offentlige kunder. Primært opgraderinger fra C# 4.7 -&amp;gt; .NET 8.0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2021-23 team.blue - senior udvikler&lt;/strong&gt;&lt;br&gt;
Videreudviklng af intern faktureringsplatform for koncernens brands og integrationer til andre systemer i en microservices arkitektur.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2021-21 mySupply - senior udvikler&lt;/strong&gt;&lt;br&gt;
Videreudvikling af dokumentudvekslingsplatform for OIOUBL / PEPPOL dokumenter for private og offentlige virksomheder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2020-20 Quickorder - senior udvikler&lt;/strong&gt;&lt;br&gt;
Fokus på videreudviklng af cloudbaseret brancheløsning til restaurantbranchen inklusive PointOfSale, booking og menuer. Primært fokus på integrationer til økonomisystemer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2010-19 CodeZoo / EG - lead udvikler&lt;/strong&gt;&lt;br&gt;
Nyudvikling af Xena - et multi-tenant regnskabssystem med åbent API. Ansvar for backend, databaseintegration og teknisk implementering af kundekrav.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2005-10 TransSoft - udvikler/konsulent&lt;/strong&gt;&lt;br&gt;
Videreudvikling af modulbaseret brancheløsning til transportbranchen bygget på Damgaard / Microsoft C5 og nyudvikling af online bookingportal af godstransport.&lt;/p&gt;
&lt;h2 id="privat"&gt;Privat&lt;/h2&gt;
&lt;p&gt;Magnus Toftdal Lund, bosat i Odense C - far til to og fra 1977.&lt;/p&gt;</description></item></channel></rss>