Så släpper vi funktioner som rör pengar utan att ändra någons pris av misstag
En prisbugg är den sorten du inte kan ta tillbaka när ett kort väl har debiterats. Här är disciplinen vi använder för att lägga till rabatter, personalprissättning och rea i bokningsflödet utan att ett enda pris rör sig innan vi vill det.
De flesta buggar kostar dig en ursäkt. En prisbugg kostar dig en återbetalning, en återkrävning och en kund som inte längre litar på siffran på skärmen. Insatserna är sneda, så processen runt allt som rör pengar är medvetet långsammare än resten av vårt arbete. Så här ser den ut, med en färsk funktion som exempel: en rea på en hel kategori, där en salong kan sätta alla ansiktsbehandlingar på rea på en gång.
Avstängd som standard, i produktion, utan att göra något
Funktionens första rad är en flagga. Den läser en miljövariabel och är avstängd som standard. Det betyder att vi kan merga och driftsätta koden långt innan den gör något, och varje pris förblir byte för byte identiskt med dagen innan. Det finns ingen stor smäll att vara nervös inför, för lanseringen är ett separat, tråkigt steg som vi tar när vi väl känner oss trygga.
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);}En funktion, en uppgift
Rabattens aritmetik bor i en enda ren funktion. Den tar ören och en procentsats, och den returnerar ören. Den har ingen databas, ingen förfrågan och ingen klocka, vilket betyder att det är trivialt att testa varje kant av den isolerat: en rea på noll, en vanlig rea, ett tak i överkanten, ett negativt värde som aldrig borde inträffa. När matematiken är en enda liten funktion du kan hålla i huvudet slutar du gissa om den.
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);}Räkna om på servern, alltid
Webbläsaren visar ett pris så att kunden vet vad hen kommer att betala, men den siffran är bara en visning. När en bokning kommer in laddar servern den riktiga tjänsten, tar fram personalpriset, tillämpar eventuell aktiv kategorirea och räknar sedan ut rabatten, allt utifrån data den själv har hämtat. Om någon har ändrat i förfrågan på vägen in ändrar det ingenting, för priset kortet debiteras var aldrig priset webbläsaren skickade. Det här är den enskilt viktigaste vanan i pengakod: förtroendegränsen är servern, och servern gör summan.
Försök knäcka den innan en kund gör det
Tester som går igenom berättar att fallen du tänkte på fungerar. De berättar inte om fallet du missade. Så innan en prisflagga slås på kör vi en fientlig genomgång vars enda uppgift är att hitta ett sätt att få pengarna fel. Separata kontroller tar var sin vinkel: en jagar varje väg som tar för mycket betalt av kunden, en jagar en väg som får företaget att förlora pengar, en kontrollerar att funktionen verkligen är passiv när flaggan är av, och en går på avrundning och gränsdrift. En rea staplad på en rabattkod på en handpenning är precis där ett brutet öre gömmer sig, så det är där vi tittar hårdast.
Inget av det här är smart. Det är en flagga, en liten funktion, en server som gör sin egen matematik och en vana att försöka knäcka sitt eget arbete innan det släpps. Tillsammans är det skillnaden mellan att lägga till en funktion som rör pengar och att ligga vaken över den.