Tilbake
8.4
Dokumentasjon og vedlikehold

8.4 Dokumentasjon og vedlikehold

Teknisk dokumentasjon, README og vedlikeholdsstrategier.

50 min
5 oppgaver
DokumentasjonREADMEVedlikeholdKommentarer
Du leser den tradisjonelle versjonen
Din fremgang i kapitlet
0 / 5 oppgaver

God dokumentasjon er nøkkelen til langsiktig vedlikehold av programvare. Kode leses langt oftere enn den skrives, og godt dokumentert kode gjør det enklere for andre (og deg selv om 6 måneder!) å forstå hva som skjer.

I dette kapittelet skal vi se på ulike former for dokumentasjon – fra kodekommentarer og docstrings til README-filer og teknisk dokumentasjon. Vi skal også diskutere teknisk gjeld og refaktorering.

Nivåer av dokumentasjon

1. Kodekommentarer

Inline-kommentarer forklarer hvorfor, ikke hva:

# DÅRLIG: Forklarer det åpenbare
x = x + 1  # Øk x med 1

# BRA: Forklarer hvorfor
x = x + 1  # Kompenserer for zero-indexed arrays

Når bruke kommentarer:
- Kompleks logikk eller algoritmer
- Workarounds for kjente bugs
- Forklare business logic
- TODOs og FIXMEs

Når IKKE bruke kommentarer:
- Istedenfor god navngiving
- Forklare selvforklarende kode
- Gammel, utdatert kode (slett i stedet!)

2. Docstrings

Docstrings dokumenterer funksjoner, klasser og moduler:

def calculate_discount(price, discount_percent, customer_tier):
    """
    Beregner rabattert pris basert på kundekategori.

    Args:
        price (float): Opprinnelig pris i NOK
        discount_percent (float): Rabattprosent (0-100)
        customer_tier (str): Kundekategori ('bronze', 'silver', 'gold')

    Returns:
        float: Rabattert pris

    Raises:
        ValueError: Hvis discount_percent er utenfor 0-100

    Example:
        >>> calculate_discount(1000, 10, 'gold')
        850.0
    """
    if not 0 <= discount_percent <= 100:
        raise ValueError("Discount må være mellom 0 og 100")

    base_discount = price * (discount_percent / 100)

    # Ekstra rabatt basert på tier
    tier_bonus = {'bronze': 0, 'silver': 0.05, 'gold': 0.10}
    extra = price * tier_bonus.get(customer_tier, 0)

    return price - base_discount - extra

Docstring-formater:
- Google style (mest lesbar)
- NumPy style (for vitenskapelig kode)
- reStructuredText (Sphinx-dokumentasjon)

3. README.md

Hver repository bør ha en README med:

# Prosjektnavn

Kort beskrivelse av hva prosjektet gjør.

## Funksjoner
- Feature 1
- Feature 2

## Installasjon
\`\`\`bash
pip install -r requirements.txt
\`\`\`

## Bruk
\`\`\`python
from myapp import MyClass
obj = MyClass()
\`\`\`

## Bidra
Se CONTRIBUTING.md

## Lisens
MIT License

4. API-dokumentasjon

For biblioteker og APIer:
- Sphinx (Python) – Genererer HTML fra docstrings
- JSDoc (JavaScript)
- Swagger/OpenAPI (REST APIs)

Teknisk gjeld (Technical Debt)

Teknisk gjeld er kompromisser i koden som gjør fremtidig utvikling vanskeligere:

Typer teknisk gjeld:
- Forsettlig: "Vi fikser dette etter lansering" (men glemmer det)
- Uforsettlig: Dårlig design pga manglende erfaring
- Miljømessig: Verktøy/biblioteker som blir utdaterte

Konsekvenser:
- Lengre tid på nye features
- Flere bugs
- Vanskelig å onboarde nye utviklere
- Lav motivasjon i teamet

Håndtere teknisk gjeld:
- Alloker tid til "refactor sprints"
- Boy Scout Rule: "Leave the code better than you found it"
- Dokumenter kjent gjeld i TECH_DEBT.md
- Prioriter gjeld som blokkerer nye features

Refaktorering

Refaktorering er å forbedre kodestrukturen uten å endre funksjonalitet:

Vanlige refaktoreringer:
- Endre variabelnavn
- Trekke ut metoder (Extract Method)
- Flytte kode til riktig klasse (Move Method)
- Fjerne duplikat kode (DRY principle)
- Forenkle komplekse if-setninger

Gylne regel: Ha tester før refaktorering!

# FØR refaktorering
def process(data):
    result = []
    for item in data:
        if item['status'] == 'active' and item['age'] > 18:
            result.append(item['name'].upper())
    return result

# ETTER refaktorering
def process(data):
    return [
        item['name'].upper()
        for item in data
        if is_eligible(item)
    ]

def is_eligible(item):
    """Sjekk om item er eligible for processing."""
    return item['status'] == 'active' and item['age'] > 18

Code smells (tegn på dårlig kode):
- Long Method – Funksjoner over 50 linjer
- God Class – Klasser som gjør for mye
- Duplicate Code – Copy-paste mellom filer
- Magic Numbers – Hardkodede tall uten forklaring
- Long Parameter List – Funksjoner med > 5 parametere

✏️Eksempel: God vs dårlig dokumentasjon
DÅRLIG dokumentasjon:

def calc(a, b, c):
    # Beregner noe
    x = a * b
    if c:
        return x * 1.25
    return x

Problemer:
- Uklare parameternavn (a, b, c)
- Uklar kommentar ("noe")
- Ingen docstring
- Magisk tall (1.25)

GOD dokumentasjon:

TAX_RATE = 1.25  # Moms 25%

def calculate_price(quantity, unit_price, include_tax):
    """
    Beregner totalpris for en bestilling.

    Args:
        quantity (int): Antall varer
        unit_price (float): Pris per vare i NOK
        include_tax (bool): Om moms skal inkluderes

    Returns:
        float: Totalpris i NOK

    Example:
        >>> calculate_price(5, 100, True)
        625.0
    """
    subtotal = quantity * unit_price

    if include_tax:
        return subtotal * TAX_RATE

    return subtotal

✏️Eksempel: Refaktorering av kompleks kode
FØR refaktorering:

def process_order(order):
    # Valider ordre
    if 'items' not in order or len(order['items']) == 0:
        return {'status': 'error', 'msg': 'Ingen varer'}
    if 'customer' not in order or 'email' not in order['customer']:
        return {'status': 'error', 'msg': 'Ugyldig kunde'}

    # Beregn total
    total = 0
    for item in order['items']:
        if 'price' not in item or 'qty' not in item:
            return {'status': 'error', 'msg': 'Ugyldig vare'}
        total += item['price'] * item['qty']

    # Sjekk lager
    for item in order['items']:
        if item['qty'] > item.get('stock', 0):
            return {'status': 'error', 'msg': f"{item['name']} ikke på lager"}

    # Rabatt hvis over 1000kr
    if total > 1000:
        total = total * 0.9

    return {'status': 'success', 'total': total}

ETTER refaktorering:

BULK_DISCOUNT_THRESHOLD = 1000
BULK_DISCOUNT_RATE = 0.10

def process_order(order):
    """Prosesserer ordre og returnerer resultat."""
    validation_error = validate_order(order)
    if validation_error:
        return {'status': 'error', 'msg': validation_error}

    total = calculate_total(order['items'])
    total = apply_bulk_discount(total)

    return {'status': 'success', 'total': total}

def validate_order(order):
    """Validerer ordre, returnerer feilmelding eller None."""
    if not order.get('items'):
        return 'Ingen varer'

    customer = order.get('customer', {})
    if not customer.get('email'):
        return 'Ugyldig kunde'

    for item in order['items']:
        if 'price' not in item or 'qty' not in item:
            return 'Ugyldig vare'

        if item['qty'] > item.get('stock', 0):
            return f"{item['name']} ikke på lager"

    return None

def calculate_total(items):
    """Beregner totalpris for alle varer."""
    return sum(item['price'] * item['qty'] for item in items)

def apply_bulk_discount(total):
    """Gir rabatt på store ordrer."""
    if total > BULK_DISCOUNT_THRESHOLD:
        return total * (1 - BULK_DISCOUNT_RATE)
    return total

Forbedringer:
- Mindre funksjoner med tydelige navn
- Konstanter istedenfor magiske tall
- Enklere å teste hver del isolert
- Enklere å forstå hva som skjer

Oppgave 1 (Flervalg)
Hva er hovedformålet med docstrings?

A) Å forklare hver eneste linje kode
B) Å dokumentere hva funksjoner, klasser og moduler gjør
C) Å gjøre koden lengre
D) Å erstatte variabelnavn

Oppgave 2 (Flervalg)
Hva er teknisk gjeld?

A) Penger man skylder til utviklere
B) Kompromisser i koden som gjør fremtidig utvikling vanskeligere
C) Antall bugs i systemet
D) Tid brukt på testing

Oppgave 3
Forklar forskjellen mellom kommentarer og docstrings i Python.

Oppgave 4
Skriv en god docstring for denne funksjonen:

def filter_users(users, min_age, country):
    return [u for u in users if u['age'] >= min_age and u['country'] == country]

Oppgave 5
Hva er "Boy Scout Rule" i programmering?

Oppgave 6
Nevn tre "code smells" (tegn på dårlig kode).

Oppgave 7
Refaktorer denne funksjonen for bedre lesbarhet:

def x(a, b):
    r = []
    for i in a:
        if i > b:
            r.append(i * 2)
    return r

Oppgave 8
Hva bør en god README.md-fil inneholde?

// --- Samleoppgaver ---

Samleoppgave 1
Du overtar et prosjekt med denne koden:

def f(d):
    t = 0
    for i in d:
        if i['s'] == 'a' and i['p'] > 100:
            t += i['p'] * i['q']
        elif i['s'] == 'b':
            t += i['p'] * i['q'] * 0.8
    if t > 5000:
        t = t * 0.95
    return t

a) Forklar hva koden gjør (gjett basert på logikken).
b) Refaktorer koden med bedre navn, konstanter og struktur.
c) Legg til passende docstrings.
d) Hvilke "code smells" fant du i originalkoden?

Samleoppgave 2
Du leder et team som vedlikeholder en 5 år gammel webapplikasjon. Teamet rapporterer:
- Ny funksjonalitet tar 3x lengre tid enn før
- Mange bugs dukker opp etter hver deploy
- Ingen tør å endre visse deler av koden
- Ingen dokumentasjon finnes

a) Hvilke tegn på teknisk gjeld ser du her?
b) Lag en plan for å adressere problemet over 3 måneder.
c) Hvilke typer dokumentasjon bør prioriteres først?
d) Hvordan kan teamet unngå å samle opp ny teknisk gjeld fremover?

Oppsummering

I dette kapittelet har du lært:

- Dokumentasjon: gjør koden forståelig for andre.
- Kodekommentarer: forklar hvorfor, ikke hva.
- README: beskriver prosjektet og bruk.
- Vedlikehold: holde koden oppdatert over tid.
- Refaktorering: forbedre kodestruktur uten å endre funksjon.

Noekkelbegreper


BegrepForklaring
DokumentasjonSkriftlig info som forklarer prosjektet
RefaktoreringForbedre kodestruktur uten å endre oppførsel

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.