fun createShipment(command: CreateShipmentCommand, notify: Boolean): Shipment {
    if (command.countryCode !in setOf("NO", "SE", "DK", "FI")) {
        throw ValidationException("Unsupported country ${command.countryCode}")
    }
    val first = command.recipientFirstName.trim().replaceFirstChar { it.uppercase() }
    val last = command.recipientLastName.trim().uppercase()
    val entity = ShipmentEntity()
    entity.id = UUID.randomUUID()
    entity.recipientName = "$first $last"
    entity.addressLine = command.addressLine
    entity.postalCode = command.postalCode.replace(" ", "")
    entity.countryCode = command.countryCode
    entity.status = "CREATED"
    entity.createdAt = Instant.now()
    for (p in command.parcels) {
        val pe = ParcelEntity()
        pe.shipmentId = entity.id
        pe.weightGrams = (p.weightKg * 1000).toInt()
        entity.parcels.add(pe)
    }
    val saved = shipmentRepository.save(entity)
    val payload = mapOf(
        "shipmentId" to saved.id.toString(),
        "status" to saved.status,
        "ts" to saved.createdAt.toEpochMilli(),
    )
    kafka.send("shipment-events", saved.id.toString(), json.writeValueAsString(payload))
    if (notify) {
        val token = tokens.findByCustomerId(command.customerId) ?: return saved.toDomain()
        push.send(PushMessage(token, "Forsendelse registrert", "Pakken din er på vei"))
    }
    return saved.toDomain()
}
Åpning
Samme metode
fun createShipment(command: CreateShipmentCommand): Shipment {
    validateShipment(command)
    val shipment = saveShipment(command)
    publishShipmentCreated(shipment)
    notifyCustomer(shipment)
    return shipment
}
Rendyrket kode
I AI-ens tidsalder
Fem synder jeg ser ofte – og spørsmålene som avslører dem
Åpning
Hva er god kode?
Kommuniserer forretningsreglene, og abstraherer bort overflødige detaljer
Hvorfor bry seg
«AI skriver jo koden min»
1
AI følger eksisterende kodestil
2
AI skriver kode ved addisjon, fremfor opprydding
3
Vi trenger fortsatt å kunne inspisere systemet
Fem faresignaler
Tenkt kundereise: Postforsendelse hjemmefra
1
Kunden registrerer forsendelse
2
Budet henter og skanner
3
Betalingsvarsel sendes
4
Kunden betaler
fun createShipment(command: CreateShipmentCommand, notify: Boolean): Shipment {
    if (command.countryCode !in setOf("NO", "SE", "DK", "FI")) {
        throw ValidationException("Unsupported country ${command.countryCode}")
    }
    val first = command.recipientFirstName.trim().replaceFirstChar { it.uppercase() }
    val last = command.recipientLastName.trim().uppercase()
    val entity = ShipmentEntity()
    entity.id = UUID.randomUUID()
    entity.recipientName = "$first $last"
    entity.addressLine = command.addressLine
    entity.postalCode = command.postalCode.replace(" ", "")
    entity.countryCode = command.countryCode
    entity.status = "CREATED"
    entity.createdAt = Instant.now()
    for (p in command.parcels) {
        val pe = ParcelEntity()
        pe.shipmentId = entity.id
        pe.weightGrams = (p.weightKg * 1000).toInt()
        entity.parcels.add(pe)
    }
    val saved = shipmentRepository.save(entity)
    val payload = mapOf(
        "shipmentId" to saved.id.toString(),
        "status" to saved.status,
        "ts" to saved.createdAt.toEpochMilli(),
    )
    kafka.send("shipment-events", saved.id.toString(), json.writeValueAsString(payload))
    if (notify) {
        val token = tokens.findByCustomerId(command.customerId) ?: return saved.toDomain()
        push.send(PushMessage(token, "Forsendelse registrert", "Pakken din er på vei"))
    }
    return saved.toDomain()
}
Faresignal 1
Flere abstraksjonsnivåer samtidig
Forretningsregel
Strengmanipulasjon
Databaseformat
Integrasjoner
Faresignal 1
Flere abstraksjonsnivåer samtidig
Kan en annen utvikler forstå omtrent hva denne metoden gjør, uten å lese alle detaljene?
Faresignal 2
Flagg skjuler ulike operasjoner
createShipment(command, true)
Faresignal 2
Flagg skjuler ulike operasjoner
val shipment = createShipment(command)
notifyCustomer(shipment)
Beskriver parameteren data – eller velger den hvilken operasjon metoden skal utføre?
Faresignal 2
To metoder, nesten like
fun registerPrivateParcels(shipment: Shipment) {
    for (parcel in shipment.parcels) {
        require(parcel.weightKg <= 35.0) { "Too heavy" }
        val label = Label(parcel.id, Barcode.CODE_128)
        label.recipient = shipment.recipient.name
        label.address = shipment.address.oneLine()
        courierClient.send(label)
        val price = BASE_FEE + parcel.weightKg * KG_RATE
        ledger.add(shipment.id, parcel.id, price)
    }
    val total = ledger.totalFor(shipment.id)
    push.send(shipment.customerId, "Betal $total kr")
}
fun registerBusinessParcels(shipment: Shipment) {
    for (parcel in shipment.parcels) {
        require(parcel.weightKg <= 35.0) { "Too heavy" }
        val label = Label(parcel.id, Barcode.CODE_128)
        label.recipient = shipment.recipient.name
        label.address = shipment.address.oneLine()
        label.reference = shipment.poNumber
        courierClient.send(label)
        val price = BASE_FEE + parcel.weightKg * KG_RATE
        ledger.add(shipment.id, parcel.id, price)
    }
    val total = ledger.totalFor(shipment.id)
    invoicing.createInvoice(shipment.customerId, total)
}
Faresignal 2
«Generalisert» med et flagg
fun registerParcels(shipment: Shipment, isBusiness: Boolean) {
    for (parcel in shipment.parcels) {
        require(parcel.weightKg <= 35.0) { "Too heavy" }
        val label = Label(parcel.id, Barcode.CODE_128)
        label.recipient = shipment.recipient.name
        label.address = shipment.address.oneLine()
        if (isBusiness) {
            label.reference = shipment.poNumber
        }
        courierClient.send(label)
        val price = BASE_FEE + parcel.weightKg * KG_RATE
        ledger.add(shipment.id, parcel.id, price)
    }
    val total = ledger.totalFor(shipment.id)
    if (isBusiness) {
        invoicing.createInvoice(shipment.customerId, total)
    } else {
        push.send(shipment.customerId, "Betal $total kr")
    }
}
Faresignal 2
Trekk ut detaljene, behold metodene
fun registerPrivateParcels(shipment: Shipment) {
    validateWeights(shipment.parcels)
    val labels = createLabels(shipment)
    sendToCourier(labels)
    val total = priceParcels(shipment)
    sendPaymentRequest(shipment.customerId, total)
}
fun registerBusinessParcels(shipment: Shipment) {
    validateWeights(shipment.parcels)
    val labels = createLabels(shipment).withReference(shipment.poNumber)
    sendToCourier(labels)
    val total = priceParcels(shipment)
    invoicing.createInvoice(shipment.customerId, total)
}
Faresignal 2
Foretrekk litt duplisering fremfor en dårlig abstraksjon.
Faresignal 3
Skjult mutasjon
fun createShipment(command: CreateShipmentCommand): Shipment {
    val shipment = buildShipment(command)
    // lag en kopi isteden!
    applyPricing(shipment) // Setter shipment.price og shipment.currency
    notifyCustomer(shipment)
    return shipment
}
Faresignal 3
Skjult mutasjon
fun createShipment(command: CreateShipmentCommand): Shipment {
    val shipment = buildShipment(command)
    val pricedShipment = shipment.withPrice(calculatePrice(shipment))
    notifyCustomer(pricedShipment)
    return pricedShipment
}
Faresignal 4
Kommentarer som oversetter koden
// Create the parcel labels
val labels = mutableListOf<Label>()
for (parcel in shipment.parcels) {
    labels.add(Label(parcel.id, BarcodeFormat.CODE_128))
}
 
// Send the labels to the courier
for (label in labels) {
    courierClient.send(label)
}
Faresignal 4
Kommentarer som oversetter koden
val labels = createParcelLabels(shipment)
sendLabelsToCourier(labels)
Faresignal 5
Parameterlisten er egentlig en datastruktur
createShipment(
    recipientFirstName,
    recipientLastName,
    addressLine,
    postalCode,
    city,
    countryCode,
)
Faresignal 5
Parameterlisten er egentlig en datastruktur
createShipment(recipient)
Er dette separate argumenter – eller data som hører sammen og mangler et navn?
AI og best practice
Hvordan få AI-en til å følge reglene?
Pre-commit hooks
Formatering og linting før hver commit
Statisk analyse fanger de vanligste syndene
Skills og AGENTS.md
Foretrekk immutable og funksjonelle mønstre
Små metodekropper
AI-en gjør oversettelsen for deg
CI
Samme sjekker på hver PR
Rødt bygg stopper koden før review
Avslutning
Tre spørsmål til neste code review
1
Kan jeg forstå forretningsflyten uten å lese detaljene?
2
Gjør navnene og API-et de mulige handlingene tydelige?
3
Kan data endres på steder jeg ikke forventer?
fun createShipment(command: CreateShipmentCommand, notify: Boolean): Shipment {
    if (command.countryCode !in setOf("NO", "SE", "DK", "FI")) {
        throw ValidationException("Unsupported country ${command.countryCode}")
    }
    val first = command.recipientFirstName.trim().replaceFirstChar { it.uppercase() }
    val last = command.recipientLastName.trim().uppercase()
    val entity = ShipmentEntity()
    entity.id = UUID.randomUUID()
    entity.recipientName = "$first $last"
    entity.addressLine = command.addressLine
    entity.postalCode = command.postalCode.replace(" ", "")
    entity.countryCode = command.countryCode
    entity.status = "CREATED"
    entity.createdAt = Instant.now()
    for (p in command.parcels) {
        val pe = ParcelEntity()
        pe.shipmentId = entity.id
        pe.weightGrams = (p.weightKg * 1000).toInt()
        entity.parcels.add(pe)
    }
    val saved = shipmentRepository.save(entity)
    val payload = mapOf(
        "shipmentId" to saved.id.toString(),
        "status" to saved.status,
        "ts" to saved.createdAt.toEpochMilli(),
    )
    kafka.send("shipment-events", saved.id.toString(), json.writeValueAsString(payload))
    if (notify) {
        val token = tokens.findByCustomerId(command.customerId) ?: return saved.toDomain()
        push.send(PushMessage(token, "Forsendelse registrert", "Pakken din er på vei"))
    }
    return saved.toDomain()
}
fun createShipment(command: CreateShipmentCommand): Shipment {
    validateShipment(command)
    val shipment = saveShipment(command)
    publishShipmentCreated(shipment)
    notifyCustomer(shipment)
    return shipment
}