Pre-review-intervju
I team er koden ikke min alene. Flere er involvert, styringen må være god, og alle skal kunne forstå hva som foregår. Da holder det ikke at appen virker.
Så før jeg leverer fra meg en PR, intervjuer Claude meg om min egen diff. Ett spørsmål om gangen, både hvordan koden virker og hvorfor den ble slik. Håndvifting godtas ikke. Grønt lys først når forståelsen sitter.
Det er en sperre, ikke et tips
Draft-PR først, så intervjuet, og først da ut av draft. En draft er ikke delt med andre — reviewere varsles først når den forlater draft. Vakler noe på slutten: ikke grønt lys.
Tiden går til det som kan svi
Renames, formattering og importer hoppes over. Igjen står det jeg ville blitt flau over å ikke forstå: type-resonnement, subtil semantikk, tradeoffs. Og særlig alt som ble foreslått av AI-en, eller som «bare funker».
Det finner svakheter i koden også
Når jeg påstår noe, kjører Claude en sjekk for å se om det stemmer — og noen ganger må vi skrive om koden etterpå. Når du har brukt et par timer på å prompte fram en løsning, er det fort gjort å se raskt over den og sende den videre.
Et ekte intervju: den forsvunne pluginen
Vi oppdaterte noen dependencies for å tilfredsstille Dependabot. Underveis fjernet
Claude io.spring.dependency-management
fra build.gradle.kts. Uten intervjuet
hadde jeg tenkt «ja ja, da trenger vi vel ikke den» og gått videre. Slik gikk det i stedet:
id("io.spring.dependency-management")
fra build.gradle.kts.
Hvorfor trenger vi den ikke lenger?
testImplementation i Gradle?
"org.eksempel:bibliotek:1.4.2".
Ingenting er løst opp på det tidspunktet.
platform(...)
bidrar med constraints; det er stemmer, ikke ordre.
...men det var jo nettopp det som ikke skjedde. Derfor satt vi og lurte i går.
io.spring.dependency-management ikke deltar i den avstemningen. Hva gjør den i stedet?
dependencyManagement er et
dekret. Så pluginen henger seg på resolusjonen og setter versjonen på
hver modul den styrer. Den stemmer ikke. Den overskriver.
Det sitter. La oss ikke tro på det — Gradle sier det selv. Jeg kjører den med og uten pluginen:
--configuration runtimeClasspath --dependency bibliotek
Selected by rule mot By conflict resolution — det er nøyaktig skillet du beskrev, med Gradles egne ord. Siste spørsmål: hva mister vi ved å fjerne den?
implementation(platform(SpringBootPlugin.BOM_COORDINATES)).
Vi beholder de samlede versjonene. Det vi mister er Maven-semantikken der BOM-en
trumfer meg, og det var jo den som blokkerte sikkerhetsoppdateringen.
Dette sitter. Du kan trygt gå til review.
Det du nå eier, og som bør stå i PR-beskrivelsen: configurations er bøtter
med strenger; versjoner avgjøres ved avstemning i resolusjonsfasen; et BOM som
platform stemmer, mens
io.spring.dependency-management
overskriver etterpå — og det er derfor overstyringen din ikke slo gjennom.
Det er dette som er forskjellen på en grønn test og en forstått endring. Testene var
grønne hele veien. Det var forklaringen som manglet — og den forklaringen er det
eneste som gjør at neste person som ser rart på
build.gradle.kts ikke legger
pluginen inn igjen.
Kortere runde: hvorfor inline her?
Ikke alle intervjuer er lange. Dette tok fire minutter, og det står igjen fordi det første svaret mitt var riktig og likevel ikke svaret.
withLock er merket
inline. Hvorfor?
Function0.invoke.
Begge deler er sant, og ingen av dem er grunnen. Se på kallstedet ditt igjen:
fun findUser(id: UserId): User? {
withLock {
cache[id]?.let { return it }
}
return loadFromDb(id)
} Hva returnerer den linja fra?
findUser. Ikke fra
lambdaen. Den hopper ut av hele funksjonen, forbi
loadFromDb.
Det er et non-local return, og det er hele grunnen. Fjern
inline og bygg:
findUser som om den var
skrevet der, så
return er et ekte return
derfra. Uten inline er
lambdaen et eget objekt med sin egen invoke,
og da finnes det ingenting å returnere fra. Ytelsen er en bieffekt.
Det jeg faktisk kjøpte var lov til å hoppe ut.
crossinline på den?
Fordi den ikke kalles direkte i bodyen til
launchTraced. Den havner inne i
launch { }:
inline fun CoroutineScope.launchTraced(
name: String,
crossinline block: suspend () -> Unit
): Job = launch { block() } Et non-local return derfra skulle returnert fra
launchTraced, som for lengst
har returnert en Job.
Stackframen finnes ikke lenger. Så jeg lover at lambdaen ikke gjør det.
Fjern den og se hva du får. Det er to feil, ikke én:
it might contain non-local returns. Add 'crossinline'
modifier to parameter declaration 'block'.
function type are not supported. Add 'noinline' or
'crossinline' modifier.
Den andre er ikke en konsekvens av den første. Hva sier den?
suspend-lambda i en
ikke-suspend inline-funksjon ikke kan inlines i det hele tatt, uansett hvor jeg
kaller den. Suspend-bodyen kompileres til en tilstandsmaskin med sin egen klasse;
den kan ikke limes inn i en vanlig funksjon. Derfor dukker
crossinline opp så ofte rundt
coroutines: det er ikke et coroutine-triks, det er den eneste veien inn.
Kompilatoren. Ikke runtime. Det er ingen sjekk som feiler i produksjon en
torsdag; det er kode som aldri blir bygget.
crossinline er en kontrakt
jeg signerer på kompileringstidspunktet, ikke en vakt som står der og passer på.
Og kompilatoren sier rett ut hva jeg skal skrive. Jeg hadde lest den meldingen et titalls ganger uten å lese den.
Alle fire kompilatorutskriftene over er kjørt, ikke husket — kotlinc 2.1.0. Det er samme regel som gjelder i intervjuet: kan en påstand sjekkes, så sjekkes den.
Tonen
Et intervju som føles som en eksamen blir besvart som en eksamen — med gjetting.
- Samarbeidende, ikke eksaminerende
- Dette beskytter utvikleren, det tar hen ikke.
- Ærlig framfor snill
- Poenget er å avdekke hull, ikke å bli fort ferdig. Et «nesten riktig» svar er nettopp det vi prøver å fange.
- Forankret
- Pek på faktiske symboler, filer og linjer i diffen. Og verifiser lokalt framfor å påstå — kjør kompilatoren, ikke hukommelsen.
Skillen
Hele greia er én markdown-fil. Legg den i
~/.claude/skills/pre-review-intervju/SKILL.md,
si «klar for review», og la være å svare for fort.
---
name: pre-review-intervju
description: Intervju utvikleren om endringene FØR de går til review, så hen forstår alt (hvordan og hvorfor) før noen andre ser det. Bruk når utvikleren er klar for review, skal opprette/oppdatere en PR, ber om å bli intervjuet, eller sier "før review"/"klar for review". Gate mot å dele noe hen ikke fullt ut eier.
---
# Pre-review intervju
Formål: Ingen skal gi fra seg en endring til review som de ikke selv forstår
fullt ut, både *hvordan* den virker og *hvorfor* den ble gjort slik. Denne skill-en
kjøres før vi går til review. Du (Claude) intervjuer utvikleren til du er trygg på
at forståelsen sitter. Dette er en sperre: ikke gi grønt lys før den er passert.
## Når den kjører
Foretrukket rekkefølge: opprett draft-PR først, kjør så dette intervjuet før PR-en
tas ut av draft / sendes til reell review. En draft er ikke "delt med andre" i den
forstand denne skill-en vokter mot — reviewere varsles først når den forlater draft.
- Standard: rett etter at draft-PR-en er opprettet/oppdatert, før den forlater draft.
- Når utvikleren sier hen er klar for review, eller ber om intervju.
- Etter at en endring er ferdig, men før den deles med andre (dvs. før draft fjernes).
Er utvikleren i ferd med å ta en PR ut av draft eller be om reell review uten å ha
kjørt denne, minn om den først.
## Slik gjør du det
1. **Kartlegg endringene.** Hent diffen (working tree, branch vs base, eller PR).
Del den i logiske enheter, én idé per bolk, ikke fil for fil.
2. **Prioriter det som kan svi.** Hopp over det trivielle (rename, formattering,
importer). Bruk tiden på det utvikleren ville blitt flau over å ikke forstå:
type-resonnement, subtil semantikk, tradeoffs, hvorfor denne tilnærmingen og
ikke en annen, hva som ville skjedd ved alternativer, og særlig alt som ble
foreslått av Claude/AI eller som "bare funker".
3. **Intervju, ikke foredrag.** Still ett fokusert spørsmål om gangen og vent på
svar i utvikleren sine egne ord. Ikke server svaret i spørsmålet. Veksle mellom:
- *Hvordan:* "Hva gjør denne linja konkret?" "Hvorfor kompilerer dette?"
- *Hvorfor:* "Hvorfor denne løsningen og ikke X?" "Hvilket problem løser den?"
- *Press som en reviewer ville:* "Hva skjer hvis ...?" "Hvorfor er dette trygt?"
"Hva er kostnaden?"
4. **Ikke godta håndvifting.** Et "nesten riktig" eller vagt svar er ikke godt nok
her, for det er nettopp det vi prøver å fange. Si rett ut at det ikke sitter,
forklar konkret, gjerne med en analogi og en repetisjon, og still spørsmålet på
nytt til det sitter. Verifiser lokalt hvis et svar kan sjekkes (kompiler, kjør),
heller enn å påstå.
5. **Avslutt med en forståelses-sjekk.** Oppsummer kort hva utvikleren har bekreftet
at hen forstår, og flagg ærlig det som fortsatt vakler. Vakler noe: ikke grønt lys.
## Tone
- Samarbeidende, ikke eksaminerende. Dette beskytter utvikleren, det tar hen ikke.
- Ærlig framfor snill: poenget er å avdekke hull, ikke å bli fort ferdig.
- Forankret: pek på faktiske symboler, filer og linjer i diffen.
## Resultat
Når alt sitter: si det rett ut ("Dette sitter, du kan trygt gå til review") og
oppsummer de viktigste poengene utvikleren nå eier, så de kan gjenbrukes i
PR-beskrivelse eller på Slack.