Voordat de factuurknop live ging, bouwden we wat hem tegenhoudt
Het systeem stond op het punt klanten te gaan mailen. Niets hield het tegen om ze allemaal te mailen.
Een Nederlandse zonweringinstallateur
Geautomatiseerd factureren was dagen verwijderd van livegang. We stopten en bouwden eerst de beheersmaatregel die de schade begrenst: één trechter waar elk uitgaand bericht doorheen moet, met idempotentie die in de database wordt vastgelegd voordat de provider ooit wordt aangeroepen.
Het vraagstuk
Uitgaande e-mail is het punt waarop een softwarefout niet langer intern is. Een bug in een rapport is gênant; een bug in een verzendlus bereikt elke klant die u hebt, permanent, in ongeveer vier seconden.
Het risico was niet hypothetisch. Toen we het draaiende systeem maten, door de live container te lezen in plaats van het configuratiebestand, stond de uitgaande poort al open. Wat klantmail feitelijk tegenhield, was een ontbrekende omgevingsvariabele. Dat is geen beheersmaatregel. Dat is geluk.
De gangbare maatregelen zijn ook precies de maatregelen die falen onder de omstandigheden waarin ze nodig zijn: een retry die dubbel verstuurt, een rate limiter die zichzelf reset, een allowlist die openvalt als de opzoeking faalt.
Wat we deden
Eén trechter, geen uitzonderingen
Elk uitgaand pad werd omgezet naar één beveiligde functie, en een test die de broncode scant houdt het aantal verzendpunten vast: er kan er niet stilletjes een bij komen.
Eerst vastleggen, dan aanroepen
Een unieke databaseconstraint legt de verzendsleutel vast voordat de e-mailprovider wordt benaderd. Mislukt het vastleggen, dan wordt de provider nooit aangeroepen. Duplicaten worden onmogelijk in plaats van onwaarschijnlijk.
Een breaker die zichzelf niet herstelt
Overschrijdingen van de rate limit laten een circuit breaker vallen zonder automatisch herstel. Een mens moet hem vrijgeven en een reden vastleggen. Een breaker die zichzelf reset, spreidt een incident alleen uit over een langer venster.
Bij twijfel altijd dicht
Kan de allowlist niet worden geëvalueerd, dan wordt er niets verstuurd. Elke override is mogelijk maar wordt geaudit, zodat het omzeilen van de bewaker een spoor achterlaat.
Repeteren tegen het echte schema
De migratie werd gerepeteerd op een wegwerpkopie van het productieschema, en elke constraint werd negatief getest: aantonen dat hij slechte invoer werkelijk weigert, niet alleen dat hij bestaat.
Hoe het werkt
Vaste volgorde
Controles lopen in een vaste volgorde: ongeldig, droogloop, goedkeuring vereist, allowlist, provider uitgeschakeld, breaker. Zo is de reden dat een bericht niet werd verstuurd altijd eenduidig.
Idempotentie op databaseniveau
De uniciteitsgarantie zit in de database, niet in de applicatielogica, zodat die standhoudt over processen, herstarts en gelijktijdige workers heen.
Expliciet inschakelen
Live verzenden vereist een positieve configuratie. Een ontbrekende instelling betekent dat er niets wordt verstuurd, niet dat alles wordt verstuurd.
Geaudite override
Er is een manier om verzending af te dwingen, omdat die er operationeel moet zijn. Die wordt gelogd met de onderbouwing erbij.
Gelijktijdigheid bewezen, niet aangenomen
Twintig gelijktijdige aanroepen om hetzelfde bericht te versturen, leveren precies één provideraanroep op. Dat wordt vastgesteld door een test, niet beargumenteerd in een document.
Rate limits bewezen
Een lus van vijftig pogingen bereikt de provider hooguit tien keer en laat vijftig auditregels achter, zodat zowel de limiet als de logging aantoonbaar zijn.
Constraints negatief getest
Elke constraint werd getest door te proberen hem te schenden. Een constraint die bestaat maar niet weigert, is erger dan geen, omdat hij wordt vertrouwd.
Gaten opgeschreven
Wat bij de overdracht nog niet was gedaan, zoals een migratie die nog niet was toegepast en een live verzending die nog niet had plaatsgevonden, werd expliciet benoemd in plaats van impliciet gelaten.
Technologie
Het resultaat
Vijf uitgaande verzendpunten werden samengebracht achter één beveiligde trechter, en daar gehouden door een test die faalt zodra er een zesde verschijnt. Vijfendertig nieuwe tests dekken specifiek de bewaker, binnen een suite van 2.347.
De gemeten beginsituatie is het deel dat het onthouden waard is: de poort stond open, en wat e-mail tegenhield was een niet-ingestelde variabele. Het draaiende systeem lezen in plaats van het configuratiebestand is het verschil tussen denken dat u een beheersmaatregel hebt en er een hebben.
Niets hiervan levert een mooie demo op. Het is het werk waardoor de factuurfunctie kan worden aangezet zonder dat iemand zijn adem inhoudt.
Er worden hier geen infrastructuur-ID's, providercredentials, inhoud van de allowlist of omgevingsdetails gepubliceerd.
Verwant werk
408 berichten in een week. Drieëndertig waren echte klanten
Eén gedeelde inbox slokte het hele bedrijf op. Het sorteren ervan was ongemerkt iemands taak geworden.
Vijfentwintig containers, twee nodes en een AI-agent met een sleutel tot alles
Een AI productie-infrastructuur laten beheren is roekeloos of goed bestuurd. Het verschil zit volledig in de grenzen.