Journal
Teknikk3 min lesetid

Slik lanserer vi funksjoner som rører penger uten å endre noens pris ved et uhell

En prisfeil er den typen du ikke kan ta tilbake når kortet først er belastet. Her er disiplinen vi bruker for å legge rabatter, ansattpriser og tilbud inn i bestillingsflyten uten at én eneste pris flytter seg før vi mener det.

UIUtviklerteamet i Bookatu

De fleste feil koster deg en unnskyldning. En prisfeil koster deg en refusjon, en tilbakeføring, og en kunde som ikke lenger stoler på tallet på skjermen. Konsekvensene er skjeve, så prosessen rundt alt som rører penger er bevisst tregere enn resten av arbeidet vårt. Slik ser den ut, med en fersk funksjon som eksempel: et kategoritilbud, der en salong kan sette alle ansiktsbehandlinger på tilbud på én gang.

REBUILT ON THE SERVER, EVERY BOOKINGBase priceStaff priceCategory saleBest couponCharged
Prisen på hver bestilling bygges i samme faste rekkefølge, og hele regnestykket gjøres på nytt på serveren.

Av som standard, i produksjon, uten å gjøre noe

Den første linjen i funksjonen er et flagg. Det leser en miljøvariabel og står som standard på av. Det betyr at vi kan flette inn og deploye koden lenge før den gjør noe som helst, og hver eneste pris er byte for byte identisk med dagen før. Det finnes ingen storslått lansering å være nervøs for, fordi lanseringen er et eget, kjedelig steg vi tar når vi er trygge.

ts
export const FEATURE_LIVE =  process.env.FEATURE_LIVE === "true"; // Read at the one place it matters. When the flag is off, the branch// is skipped entirely: no extra query, no price change, nothing.if (FEATURE_LIVE) {  const pct = await activeCategorySalePercent(orgId, "service", service.category);  if (pct > 0) fullPriceCents = applyCategorySaleCents(fullPriceCents, pct);}
1Behind a flagoff by default2Merge & deploystill off3Audit passes0 money bugs4Flip the flagthe go-liveLive
Lanseringen er et eget, kjedelig steg. Koden går ut lenge før den gjør noe.

Én funksjon, én jobb

Regnestykket for rabatten ligger i én eneste ren funksjon. Den tar imot øre og en prosent, og den returnerer øre. Den har ingen database, ingen forespørsel og ingen klokke, som betyr at det er trivielt å teste hver ytterkant av den isolert: et nulltilbud, et vanlig tilbud, en øvre grense, et negativt tall som aldri skal kunne skje. Når regningen er én liten funksjon du kan holde i hodet, slutter du å gjette på den.

ts
export function applyCategorySaleCents(  baseCents: number,  salePercent: number | null | undefined,): number {  const base = cents(baseCents);  const pct = Math.max(0, Math.min(100, Math.round(salePercent ?? 0)));  if (pct === 0) return base;  return cents((base * (100 - pct)) / 100);}

Regn alltid ut på nytt på serveren

Nettleseren viser en pris så kunden vet hva de skal betale, men det tallet er aldri annet enn en visning. Når en bestilling kommer inn, henter serveren den ekte tjenesten, finner prisen for medarbeideren, legger på et eventuelt aktivt kategoritilbud og regner så ut rabatten, alt sammen fra data den har hentet selv. Hvis noen har endret forespørselen på veien inn, endrer det ingenting, fordi prisen kortet belastes med aldri var prisen nettleseren sendte. Dette er den aller viktigste vanen i kode som håndterer penger: tillitsgrensen er serveren, og serveren gjør regnestykket.

BROWSERShows a price so the clientknows what they will pay.A display only. Never trusted.requestSERVER · SOURCE OF TRUTH1 · Load the real service2 · Resolve staff + sale price3 · Apply the best coupon4 · Charge exactly that
Tallet nettleseren viser er bare en visning. Serveren henter sine egne data og gjør regnestykket på nytt.

Prøv å knekke det før en kunde gjør det

Tester som går grønt forteller deg at tilfellene du tenkte på fungerer. De sier ingenting om tilfellet du overså. Så før et prisflagg slås på, kjører vi en fiendtlig gjennomgang som bare har én jobb: å finne en måte å gjøre pengene feil på. Egne sjekker tar hver sin vinkel. Én jakter på enhver vei som tar for mye av kunden, én jakter på en vei som taper penger for bedriften, én sjekker at funksjonen virkelig er død når flagget er av, og én går løs på avrunding og glidning i grensetilfellene. Et tilbud oppå en kupong oppå et depositum er nøyaktig der et brøkøre gjemmer seg, så det er der vi leter hardest.

New pricing featureOvercharges?Loses money?Leaks when off?Rounding drift?Clean sweep → GO
Fire skeptikere, hver med sin måte å ta feil på. Bare full pott er et GO.

Ingenting av dette er smart. Det er et flagg, en liten funksjon, en server som gjør sitt eget regnestykke, og en vane med å prøve å knekke sitt eget arbeid før det går ut. Til sammen er det forskjellen på å legge til en funksjon som rører penger og å ligge våken av den.

feature flagstrygg kode for betalingerdesign av prismotorvalidering på serverentillitsgrense
Klar til å ta dette i bruk?

Bookatu gir deg en bookingside med ditt eget preg, depositum, medlemskap, gavekort og påminnelser, med 0 % provisjon på bookingene dine.

Kom i gang gratis