Vårt delingshjørne
10 grep som forbedrer .NET kodebasen din
10 grep som forbedrer .NET kodebasen din
10 grep som forbedrer .NET kodebasen din





Steinar
Kollerud
Utvikler
Utvikler
12. februar 2026
Utvikling
De fleste kodebaser blir ikke kompliserte fordi noen ønsker det slik. Det skjer gradvis, gjennom små valg som virker fornuftige der og da, men som over tid gjør helheten tyngre å forstå og vanskeligere å endre. Resultatet er ofte mer kompleksitet enn verdi.
I en serie på LinkedIn delte jeg ti enkle tips jeg selv bruker for å holde kodebasene mine ryddige, forståelige og enkle å jobbe i. Denne artikkelen samler og oppsummerer de samme tipsene på ett sted. Fellesnevneren er bevisste valg, lav terskel for å gjøre ting enkelt og en praktisk tilnærming til kvalitet i hverdagen.
1️⃣ File-scoped namespaces og top-level statements
Enkle grep som reduserer nesting og forbedrer lesbarheten.
File-scoped vs block-scoped namespaces
Med file-scoped får man mindre støy og ett nivå mindre innrykk. Når du har mange filer og mye kode, synes jeg dette gjør en merkbar forskjell.
// Block-scoped namespaces namespace MyApp.Services { public class UserService { public void DoSomething() { } } } // File-scoped namespaces namespace MyApp.Services; public class UserService { public void DoSomething
Top-level statments
Ingen ekstra klasser, ingen unødvendig boilerplate. Bare det som faktisk skjer når applikasjonen starter. Små endringer, men jeg opplever at koden blir både ryddigere og mer fokusert på det som er viktig.
// Old Program.cs public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } // Top-level statement var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.Run
2️⃣ Collection expressions
Med nyere C# kan du skrive collections på en enklere og mer direkte måte. Personlig synes jeg det skaper mindre støy i koden gjør det lettere å se hva som faktisk skjer. Dette er spesielt fint i tester og konfigurasjon.
// Without collection expression var roles = new string[] { "Admin", "Editor", "User" }; var numbers = new List<int> { 1, 2, 3, 4 }; CalculateAverage(new int[] { 1, 2, 3, 4 }); // With collection expression string[] roles = ["Admin", "Editor", "User"]; List<int> numbers = [1, 2, 3, 4]; CalculateAverage([5, 8, 13
3️⃣ Early returns for å unngå nesting
Dyp nesting gjør kode unødvendig vanskelig å lese. Ofte blir flyten klarere hvis du returnerer tidlig og det er som regel lettere å forstå enn flere nivåer med if-blokker.
// Nesting if (user.IsActive) { Process(user); } // Early return if (!user.IsActive) { return; } Process(user
4️⃣ Velg én stil for if-setninger
Ulike stiler i samme kodebase skaper visuell støy og unødvendige diskusjoner i PR-er. Velg én stil (helst med klammer synes jeg), men poenget er ikke hvilken stil du velger, men at hele teamet gjør det samme. Personlig foretrekker jeg alltid klammer. Små valg som dette virker kanskje trivielle, men over tid har de stor effekt på kvaliteten på kodebasen.
// Avoid mixing – use one style if (a) DoA(); if (b) { DoB
5️⃣ is null fremfor == null
Det er en liten detalj, men jeg foretrekker konsekvent is null når jeg sjekker mot null. Det er eksplisitt, lesbart og unngår overraskelser med overloadede operatorer. For meg signaliserer det tydelig intensjon: dette er en ren null-sjekk, ingenting annet.
// == null if (user == null) { return; } // is null if (user is null) { return
6️⃣ Pattern matching med måtehold
Pattern matching gjør mange klassiske if-sjekker både kortere og lettere å lese.I stedet for å sjekke type, null og properties i flere steg, kan du ofte uttrykke hele intensjonen i én setning.Jeg opplever at koden blir mer deklarativ og at man raskere forstår hva som er gyldig input, ikke bare hvordan det sjekkes.
// Without pattern matching if (order != null && order.Customer != null && order.Customer.IsActive && order.Total > 0) { Process(order); } // With pattern matching if (order is { Customer.IsActive: true, Total: > 0 }) { Process(order
7️⃣ Records for immutable data
Når noe i koden representerer data og ikke oppførsel, bruker jeg ofte records. De gir mening semantisk, er lette å lese og fungerer veldig godt sammen med pattern matching.
At immutability, ToString() med verdier og value-based equality kommer “gratis” er også et stort pluss.
public class User { public Guid Id { get; set; } public string Name { get; set; } public string Email { get; set; } } public record User(Guid Id, string Name, string Email
8️⃣ Alt trenger ikke et interface
Interfaces er nyttige, men ikke alltid nødvendige. Hvis du kun har én implementasjon, kan det fort bli unødvendig støy. Noe av det første jeg fikk drillet inn som .NET-utvikler var at alt burde ha et interface, men over tid har jeg innsett at interface-bonanzaen ofte bare fører til mer rot og kompleksitet i kodebasen.
// Interfaces for everything public interface IUserService { User GetById(Guid id); } public class UserService : IUserService { public User GetById(Guid id) => ... } // No interfaces public class UserService { public User GetById(Guid id
9️⃣ Bruk LINQ, men ikke overdriv
LINQ er veldig uttrykksfullt, men balansen mellom lesbarhet og nytte er hårfin.
// Not so good use cases for LINQ users .Where(u => u.IsActive) .ToList() .ForEach(u => SendEmail(u)); var result = data .SelectMany(x => x.Items .Where(i => i.IsValid) .Select(i => new { x.Id, Value = i.Values .Where(v => v > 0) .Select(v => v * x.Multiplier) .Sum() })) .Where(x => x.Value > 100) .OrderByDescending(x => x.Value) .ToList
// Good use cases for LINQ var ordersPerCustomer = orders .GroupBy(o => o.CustomerId) .Select(g => new { CustomerId = g.Key, TotalOrders = g.Count(), TotalAmount = g.Sum(o => o.Total) }) .ToList(); var activeUsers = users .Where(u => u.IsActive) .Select(u => new UserDto(u.Id, u.Email)) .ToList
🔟 Tredjeparts pakker kan skape utfordringer over tid
Tredjepartsbiblioteker løser ofte reelle problemer, men de kommer også med en langsiktig kostnad. Oppgraderinger, breaking changes og inkonsistent bruk på tvers av løsninger dukker ofte opp senere. Når plattformen tilbyr god nok funksjonalitet, foretrekker jeg å bruke den. Færre avhengigheter gir som regel færre overraskelser.
// No longer needed in most modern .NET apps using Newtonsoft.Json; // replaced by System.Text.Json using AutoMapper; // explicit mapping is often clearer using Polly; // basic resiliency is built into HttpClientFactory // Built-in alternatives using System.Text.Json; using System.Net.Http; using Microsoft.Extensions.Http
Til syvende og sist handler dette ikke om syntax eller personlige preferanser. Det handler om friksjon. Hvor lett det er å lese koden, endre den og stole på den. De fleste forbedringer i en kodebase kommer ikke fra store omskrivinger eller nye rammeverk, men fra små, konsekvente valg gjort hver eneste dag.
Enklere kode er ikke mindre profesjonell kode. Det er som regel motsatt. Den er lettere å forstå for neste utvikler, lettere å teste, lettere å videreutvikle. Og viktigst: den holder over tid.
Det er fort gjort å jage det smarte, det generiske og det fleksible. Ofte er det beste du kan gjøre å stoppe opp og spørre: gjør vi dette fordi det faktisk trengs, eller fordi vi som utviklere har vanskelig for å la være?
De fleste kodebaser blir ikke kompliserte fordi noen ønsker det slik. Det skjer gradvis, gjennom små valg som virker fornuftige der og da, men som over tid gjør helheten tyngre å forstå og vanskeligere å endre. Resultatet er ofte mer kompleksitet enn verdi.
I en serie på LinkedIn delte jeg ti enkle tips jeg selv bruker for å holde kodebasene mine ryddige, forståelige og enkle å jobbe i. Denne artikkelen samler og oppsummerer de samme tipsene på ett sted. Fellesnevneren er bevisste valg, lav terskel for å gjøre ting enkelt og en praktisk tilnærming til kvalitet i hverdagen.
1️⃣ File-scoped namespaces og top-level statements
Enkle grep som reduserer nesting og forbedrer lesbarheten.
File-scoped vs block-scoped namespaces
Med file-scoped får man mindre støy og ett nivå mindre innrykk. Når du har mange filer og mye kode, synes jeg dette gjør en merkbar forskjell.
// Block-scoped namespaces namespace MyApp.Services { public class UserService { public void DoSomething() { } } } // File-scoped namespaces namespace MyApp.Services; public class UserService { public void DoSomething
Top-level statments
Ingen ekstra klasser, ingen unødvendig boilerplate. Bare det som faktisk skjer når applikasjonen starter. Små endringer, men jeg opplever at koden blir både ryddigere og mer fokusert på det som er viktig.
// Old Program.cs public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } // Top-level statement var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.Run
2️⃣ Collection expressions
Med nyere C# kan du skrive collections på en enklere og mer direkte måte. Personlig synes jeg det skaper mindre støy i koden gjør det lettere å se hva som faktisk skjer. Dette er spesielt fint i tester og konfigurasjon.
// Without collection expression var roles = new string[] { "Admin", "Editor", "User" }; var numbers = new List<int> { 1, 2, 3, 4 }; CalculateAverage(new int[] { 1, 2, 3, 4 }); // With collection expression string[] roles = ["Admin", "Editor", "User"]; List<int> numbers = [1, 2, 3, 4]; CalculateAverage([5, 8, 13
3️⃣ Early returns for å unngå nesting
Dyp nesting gjør kode unødvendig vanskelig å lese. Ofte blir flyten klarere hvis du returnerer tidlig og det er som regel lettere å forstå enn flere nivåer med if-blokker.
// Nesting if (user.IsActive) { Process(user); } // Early return if (!user.IsActive) { return; } Process(user
4️⃣ Velg én stil for if-setninger
Ulike stiler i samme kodebase skaper visuell støy og unødvendige diskusjoner i PR-er. Velg én stil (helst med klammer synes jeg), men poenget er ikke hvilken stil du velger, men at hele teamet gjør det samme. Personlig foretrekker jeg alltid klammer. Små valg som dette virker kanskje trivielle, men over tid har de stor effekt på kvaliteten på kodebasen.
// Avoid mixing – use one style if (a) DoA(); if (b) { DoB
5️⃣ is null fremfor == null
Det er en liten detalj, men jeg foretrekker konsekvent is null når jeg sjekker mot null. Det er eksplisitt, lesbart og unngår overraskelser med overloadede operatorer. For meg signaliserer det tydelig intensjon: dette er en ren null-sjekk, ingenting annet.
// == null if (user == null) { return; } // is null if (user is null) { return
6️⃣ Pattern matching med måtehold
Pattern matching gjør mange klassiske if-sjekker både kortere og lettere å lese.I stedet for å sjekke type, null og properties i flere steg, kan du ofte uttrykke hele intensjonen i én setning.Jeg opplever at koden blir mer deklarativ og at man raskere forstår hva som er gyldig input, ikke bare hvordan det sjekkes.
// Without pattern matching if (order != null && order.Customer != null && order.Customer.IsActive && order.Total > 0) { Process(order); } // With pattern matching if (order is { Customer.IsActive: true, Total: > 0 }) { Process(order
7️⃣ Records for immutable data
Når noe i koden representerer data og ikke oppførsel, bruker jeg ofte records. De gir mening semantisk, er lette å lese og fungerer veldig godt sammen med pattern matching.
At immutability, ToString() med verdier og value-based equality kommer “gratis” er også et stort pluss.
public class User { public Guid Id { get; set; } public string Name { get; set; } public string Email { get; set; } } public record User(Guid Id, string Name, string Email
8️⃣ Alt trenger ikke et interface
Interfaces er nyttige, men ikke alltid nødvendige. Hvis du kun har én implementasjon, kan det fort bli unødvendig støy. Noe av det første jeg fikk drillet inn som .NET-utvikler var at alt burde ha et interface, men over tid har jeg innsett at interface-bonanzaen ofte bare fører til mer rot og kompleksitet i kodebasen.
// Interfaces for everything public interface IUserService { User GetById(Guid id); } public class UserService : IUserService { public User GetById(Guid id) => ... } // No interfaces public class UserService { public User GetById(Guid id
9️⃣ Bruk LINQ, men ikke overdriv
LINQ er veldig uttrykksfullt, men balansen mellom lesbarhet og nytte er hårfin.
// Not so good use cases for LINQ users .Where(u => u.IsActive) .ToList() .ForEach(u => SendEmail(u)); var result = data .SelectMany(x => x.Items .Where(i => i.IsValid) .Select(i => new { x.Id, Value = i.Values .Where(v => v > 0) .Select(v => v * x.Multiplier) .Sum() })) .Where(x => x.Value > 100) .OrderByDescending(x => x.Value) .ToList
// Good use cases for LINQ var ordersPerCustomer = orders .GroupBy(o => o.CustomerId) .Select(g => new { CustomerId = g.Key, TotalOrders = g.Count(), TotalAmount = g.Sum(o => o.Total) }) .ToList(); var activeUsers = users .Where(u => u.IsActive) .Select(u => new UserDto(u.Id, u.Email)) .ToList
🔟 Tredjeparts pakker kan skape utfordringer over tid
Tredjepartsbiblioteker løser ofte reelle problemer, men de kommer også med en langsiktig kostnad. Oppgraderinger, breaking changes og inkonsistent bruk på tvers av løsninger dukker ofte opp senere. Når plattformen tilbyr god nok funksjonalitet, foretrekker jeg å bruke den. Færre avhengigheter gir som regel færre overraskelser.
// No longer needed in most modern .NET apps using Newtonsoft.Json; // replaced by System.Text.Json using AutoMapper; // explicit mapping is often clearer using Polly; // basic resiliency is built into HttpClientFactory // Built-in alternatives using System.Text.Json; using System.Net.Http; using Microsoft.Extensions.Http
Til syvende og sist handler dette ikke om syntax eller personlige preferanser. Det handler om friksjon. Hvor lett det er å lese koden, endre den og stole på den. De fleste forbedringer i en kodebase kommer ikke fra store omskrivinger eller nye rammeverk, men fra små, konsekvente valg gjort hver eneste dag.
Enklere kode er ikke mindre profesjonell kode. Det er som regel motsatt. Den er lettere å forstå for neste utvikler, lettere å teste, lettere å videreutvikle. Og viktigst: den holder over tid.
Det er fort gjort å jage det smarte, det generiske og det fleksible. Ofte er det beste du kan gjøre å stoppe opp og spørre: gjør vi dette fordi det faktisk trengs, eller fordi vi som utviklere har vanskelig for å la være?
Kanskje du liker:
Kanskje du liker:
Kanskje du liker:
Utvikling
12. februar 2026
Kodebaser blir sjelden komplekse over natten. Det skjer gradvis, gjennom små valg vi tar hver dag. Litt ekstra abstraksjon her, et interface der, en pakke som virker fornuftig i øyeblikket.
Utvikling
12. februar 2026
Kodebaser blir sjelden komplekse over natten. Det skjer gradvis, gjennom små valg vi tar hver dag. Litt ekstra abstraksjon her, et interface der, en pakke som virker fornuftig i øyeblikket.
Utvikling
4. februar 2026
Sondre tok oss gjennom hvordan OAuth og OpenID Connect fungerer når man skal implementere Authorization Code Flow.
Utvikling
4. februar 2026
Sondre tok oss gjennom hvordan OAuth og OpenID Connect fungerer når man skal implementere Authorization Code Flow.
Utvikling
8. januar 2026
Jeg opplever stadig oftere at arkitekturvalg i systemutvikling styres av problemer som kanskje kan oppstå om fem eller ti år. Vi designer for ekstrem skalerbarhet, høy last og komplekse scenarioer lenge før det finnes reelle behov for det. Ofte skjer dette på bekostning av å løse de faktiske problemene vi har her og nå.
Utvikling
8. januar 2026
Jeg opplever stadig oftere at arkitekturvalg i systemutvikling styres av problemer som kanskje kan oppstå om fem eller ti år. Vi designer for ekstrem skalerbarhet, høy last og komplekse scenarioer lenge før det finnes reelle behov for det. Ofte skjer dette på bekostning av å løse de faktiske problemene vi har her og nå.
Utvikling
12. februar 2026
Kodebaser blir sjelden komplekse over natten. Det skjer gradvis, gjennom små valg vi tar hver dag. Litt ekstra abstraksjon her, et interface der, en pakke som virker fornuftig i øyeblikket.
Utvikling
4. februar 2026
Sondre tok oss gjennom hvordan OAuth og OpenID Connect fungerer når man skal implementere Authorization Code Flow.
Utvikling
12. februar 2026
Kodebaser blir sjelden komplekse over natten. Det skjer gradvis, gjennom små valg vi tar hver dag. Litt ekstra abstraksjon her, et interface der, en pakke som virker fornuftig i øyeblikket.
Utvikling
4. februar 2026
Sondre tok oss gjennom hvordan OAuth og OpenID Connect fungerer når man skal implementere Authorization Code Flow.
Copyright ©2026. All rights reserved.
Copyright ©2026. All rights reserved.
Copyright ©2026. All rights reserved.
Copyright ©2026. All rights reserved.


