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.

Footer Logo

Når riktig partner utgjør all forskjell



822 704 042

Pløens gate 7

0181 Oslo

hei@alv.no

+47 91 92 92 18

Copyright ©2026. All rights reserved.

Footer Logo

Når riktig partner utgjør all forskjell



822 704 042

Pløens gate 7

0181 Oslo

hei@alv.no

+47 91 92 92 18

Copyright ©2026. All rights reserved.

Footer Logo

Når riktig partner utgjør all forskjell



822 704 042

Pløens gate 7

0181 Oslo

hei@alv.no

+47 91 92 92 18

Copyright ©2026. All rights reserved.

Footer Logo

Når riktig partner utgjør all forskjell



822 704 042

Pløens gate 7

0181 Oslo

hei@alv.no

+47 91 92 92 18

Copyright ©2026. All rights reserved.