Het idee in drie zinnen. Breng eerst één werkende verbinding tot stand van bron naar bestemming, met zo weinig mogelijk variatie en bijkomende complexiteit, en toon aan dat het resultaat daadwerkelijk aankomt. Voeg daarna telkens één relevante faalmodus toe. Zo blijft een grote integratie beheersbaar, omdat je de onzekerheden van elkaar scheidt in plaats van ze allemaal tegelijk te introduceren.
De naam is ontleend aan de zeevaart. Wanneer twee schepen op zee brandstof of voorraden willen overbrengen, brengen zij eerst één dunne lijn tussen beide schepen aan. Die lijn draagt zelf niets zwaars; zij brengt alleen de verbinding met het andere schip tot stand. Daarna kunnen zwaardere kabels worden overgetrokken en uiteindelijk de slangen. De eerste draad is dun, maar echt: hij bereikt de overkant, of hij bereikt die niet. Hetzelfde principe blijkt verrassend goed te werken voor interfaces, imports en gegevensstromen.
Geen nieuwe wet, maar een bruikbare methode
Het zou overdreven zijn om te beweren dat met Thin Wire een volledig nieuwe ontwikkelmethode is ontdekt. De methode bouwt voort op bekende ideeën: incrementele ontwikkeling, tracer-bullet development, walking skeletons, vertical slicing, set-georiënteerde ETL en end-to-endverificatie. De onderscheidende waarde zit niet in een van die afzonderlijke elementen, maar in de combinatie, de formulering en een handvol praktische regels.
Het onderliggende probleem is oud:
Hoe bouw je doelgericht aan een groot einddoel zonder dat de samenhang, de eigenschappen en de nog te nemen beslissingen onderweg onbeheersbaar worden?
Of, om de bekende metafoor te gebruiken: hoe eet je een olifant?
Het antwoord luidt: hap voor hap.
Maar daarmee weet je nog niet welke hap je als eerste moet nemen, langs welke as je het geheel moet opdelen of wat die eerste stap precies moet opleveren.
Wat maak je dun?
Daarin verschillen de bekende benaderingen van elkaar. Het helpt om ze eenmalig naast elkaar te zetten.
Een MVP maakt het product dun: het beperkt de functionaliteit tot wat nodig is om vast te stellen of de beoogde waarde bestaat.
Een walking skeleton maakt het werkende systeem dun: het realiseert alleen de minimale end-to-enddoorsnede die nodig is om aan te tonen dat de belangrijkste componenten samen kunnen functioneren.
Thin Wire maakt de bouwvolgorde dun: eerst wordt één kleine maar echte gegevensstroom aangelegd, waarna deze gecontroleerd wordt uitgebreid.
Het onderscheid is niet absoluut. Een MVP kan een walking skeleton bevatten, een walking skeleton kan met behulp van een thin wire worden opgebouwd en een thin wire kan onderdeel zijn van een MVP.
Het zijn geen concurrerende doctrines. Het zijn drie manieren om hetzelfde onderliggende probleem te adresseren: voorkomen dat de eigenschappen, afhankelijkheden en beslissingen van een groot einddoel al zo vroeg met elkaar verstrengeld raken dat niemand het geheel nog betrouwbaar kan overzien.
Wanneer je de methode gebruikt — en wanneer niet
Voordat het over de regels gaat, eerst de vraag of je hier iets aan hebt. Thin Wire is een methode, geen natuurwet.
De methode is vooral bruikbaar wanneer:
- bron en bestemming niet in één oogopslag te begrijpen zijn;
- fouten pas aan de andere kant van een interface zichtbaar worden;
- de brondata onbekend, inconsistent of vervuild is;
- de payload verschillende soorten complexiteit combineert;
- technische en functionele beslissingen met elkaar verstrengeld dreigen te raken.
Wanneer bron en bestemming stabiel zijn, het contract al is bewezen en het team meerdere vergelijkbare interfaces heeft gebouwd, kan een grotere eerste stap volkomen rationeel zijn.
De methode schrijft niet voor dat er altijd langzaam moet worden gewerkt. Zij schrijft voor dat je langzaam genoeg begint zolang de aard van het probleem nog onzeker is.
Bij bidirectionele interfaces is het doorgaans verstandig om eerst één richting werkend te krijgen. Bij voorkeur is dat de richting met het duidelijkste eigenaarschap, de eenvoudigste landing en de grootste leerwaarde.
Zodra twee systemen dezelfde onderliggende werkelijkheid mogen wijzigen, ontstaan vragen over:
- het bronsysteem;
- conflictresolutie;
- gelijktijdige wijzigingen;
- synchronisatielussen;
- het eigenaarschap van afzonderlijke properties.
Die vragen veranderen de aard van de interface zelf. Zij moeten doorgaans pas worden geïntroduceerd wanneer de eerste richting goed wordt begrepen.
Wie mag de draad dun maken?
De vorige sectie ging over de vraag wanneer de methode iets oplevert. Daaraan gaat een andere vraag vooraf: mag je haar überhaupt toepassen?
Thin Wire veronderstelt namelijk iets wat zelden hardop wordt gezegd. De methode gaat ervan uit dat je de draad mág laten groeien: dat je vandaag een sleutel en één waarde overstuurt en morgen twee scalars toevoegt, zonder dat daar iets anders voor nodig is dan een commit.
Die aanname is gratis zolang beide oevers van jou zijn.
Zodra de overkant van iemand anders is, verandert de prijs van iedere uitbreiding. Een extra veld is dan geen commit meer, maar een verzoek: een change request, een plek op andermans backlog, een release-cyclus, soms een contractwijziging. Twintig faalmodusklassen worden dan twintig onderhandelingen.
De methode lost in dat geval het verkeerde probleem op. Het knelpunt is niet langer de bouwvolgorde, maar de doorlooptijd van een afspraak.
Hoe zwaar dat weegt, hangt af van hoe lang die doorlooptijd is.
Bezit, compleetheid of tempo — je hebt er één van nodig
Toch werkt een import uit een extern product wel, terwijl je daar alleen de bestemming bezit.
Het verschil zit niet in eigendom, maar in wat één iteratie aan de overkant kost:
- Beide oevers van jou. De overkant beweegt mee en dat kost een commit. Hier werkt de methode het best, omdat je ook de bouwvolgorde van het contract zelf bepaalt.
- Een externe bron die al af is. De overkant hoeft niets te doen. De payload ligt er, compleet, met alle velden en alle rariteiten erin. Je vraagt niemand iets; je verwerkt alleen. Jouw landing mag dun beginnen, ook al is de bron dat niet.
- Twee teams die nauw samenwerken. De overkant moet meebewegen, maar kan dat binnen een dag. Dan werkt de methode nog steeds — mits dat tempo gehaald blijft worden.
- Een koppelvlak achter een change-proces. De overkant moet meebewegen en iedere wijziging kost weken. Hier breekt de methode.
Daaruit volgt de randvoorwaarde:
Aan de overkant heb je zeggenschap nodig, of een af contract, of een samenwerking die snel genoeg itereert. Heb je geen van drieën, dan is de eerste opgave niet bouwen, maar dat contract compleet krijgen.
Compleetheid aan de overkant vervangt zeggenschap over de overkant. Een vastgelegde, geversioneerde specificatie met een mock waar je tegenaan kunt ontwikkelen, doet voor jou wat eigendom zou doen: zij maakt de overkant stabiel genoeg om er stap voor stap naartoe te bouwen.
Pas daarna begint Thin Wire, aan jouw kant van de rivier, waar je wel de baas bent.
Dat is geen ontsnapping aan de methode, maar een verschuiving van waar zij aangrijpt. De draad blijft dun, de landing blijft echt en de uitbreiding volgt nog steeds één faalmodusklasse tegelijk. Alleen is het contract dan een gegeven in plaats van een ontwerpruimte.
Tempo als derde weg: nauw samenwerkende teams
De derde mogelijkheid verdient aparte aandacht, omdat zij de enige is waarin twee partijen het koppelvlak sámen dun houden.
Thin Wire ruilt namelijk weinig grote onzekerheden in voor veel kleine wijzigingen. Bij één team is die prijs onzichtbaar: een extra faalmodusklasse is een commit tussen de middag. Bij twee teams wordt hij zichtbaar en telt hij op. Een wijziging blijft alleen klein als zij ook klein blijft in doorlooptijd.
Dat maakt het een harde randvoorwaarde, geen wens. Beide teams moeten nauw kunnen samenwerken, en het ook willen:
- Kunnen is infrastructuur en organisatie: een gedeelde omgeving om tegenaan te testen, kort-cyclisch kunnen releasen, rechtstreeks contact tussen de mensen die het bouwen, en geen goedkeuringsproces tussen twee opeenvolgende stappen.
- Willen is prioriteit en belang: beide teams rekenen het koppelvlak tot hun eigen werk, ze worden niet per wijziging afgerekend, en niemand hoeft een verzoek eerst te verantwoorden voordat het opgepakt wordt.
Valt een van beide weg, dan valt de situatie terug op de vierde: iedere uitbreiding wordt een verzoek met een doorlooptijd, en dan is een compleet contract vooraf goedkoper dan twintig kleine stappen.
Dit is dus geen zachtere variant van de randvoorwaarde. Het is dezelfde eis, anders ingevuld: de overkant moet stabiel genoeg zijn om naartoe te bouwen. Dat lukt door haar te bezitten, door haar af te hebben, of doordat zij snel genoeg reageert om het verschil niet te laten voelen.
De lijn ging pas over omdat het protocol er al lag
De metafoor bevat dat antwoord zelf.
Replenishment at sea gebeurt juist tussen twee verschillende schepen, vaak van verschillende marines. Dat werkt niet omdat beide bemanningen ter plekke iets afspreken, maar omdat de koppelingen, de afmetingen en de procedures al decennia in een gedeelde standaard vastliggen.
De dunne lijn is niet de eerste afspraak. Hij is de eerste uitvoering van een afspraak die er al lang was.
Vier regels die het werk doen
De waarde van Thin Wire zit in de manier waarop die eerste draad wordt aangelegd. Vier regels dragen het grootste deel van het gewicht.
1. Dun betekent niet voorlopig
De draad is dun omdat er weinig overheen gaat, niet omdat de landing kunstmatig mag zijn.
De landing moet echt zijn:
- de werkelijke doeltabel;
- echte constraints;
- echte foreign keys;
- een daadwerkelijk contract;
- een controleerbaar eindresultaat.
Zonder deze voorwaarde wordt ‘dun’ al snel een excuus om alles in een JSON-blob op te slaan, een generieke stagingtabel te gebruiken of te beloven dat de normalisatie later wel wordt geregeld.
De methode zegt niet:
Bouw eerst iets voorlopigs.
Zij zegt:
Bouw eerst iets kleins dat daadwerkelijk landt.
De eerste draad hoeft niet alles te doen, maar wat hij doet, moet echt zijn.
2. Begin met de kleinste werkende propertyset en breid uit per faalmodusklasse
De eerste draad bevat alleen de kleinste verzameling properties waarmee daadwerkelijk iets van bron naar bestemming kan worden overgebracht.
Dat kan in het uiterste geval niet meer zijn dan een sleutel en één eenvoudige waarde. Het doel van die eerste stap is niet om al een bruikbaar of representatief record op te bouwen, maar om vast te stellen dat de route bestaat: de bron kan iets aanbieden, de wire kan het vervoeren en de bestemming kan het correct laten landen.
Zodra die minimale propertyset werkt, wordt de draad stap voor stap uitgebreid.
De vraag is vervolgens niet simpelweg welke property als volgende aan de beurt is, maar welke faalmodusklasse verantwoord kan worden toegevoegd.
Een faalmodusklasse is een groep properties die op dezelfde technische manier worden verwerkt en daardoor ook op vergelijkbare wijze kunnen mislukken. Ze doorlopen dezelfde soort validatie, hebben vergelijkbare afhankelijkheden en vragen bij fouten doorgaans om hetzelfde type beslissing.
Eenvoudige scalars vormen bijvoorbeeld vaak één faalmodusklasse. Ze worden rechtstreeks uitgelezen, op type, lengte of nullability gecontroleerd en vervolgens opgeslagen. Meerdere van zulke velden kunnen daarom meestal in één stap worden toegevoegd.
Foreign keys vormen een andere faalmodusklasse. Daarbij moet niet alleen een waarde worden opgeslagen, maar ook worden vastgesteld of de bijbehorende referentie bestaat, kan worden gevonden of eerst moet worden aangemaakt.
Hetzelfde geldt voor:
- lookups;
- arrays;
- conditionele logica;
- samengestelde sleutels;
- asynchrone verwerking;
- retries;
- incomplete records.
De uitbreiding volgt dus niet noodzakelijk één property per stap. Zij volgt één samenhangende categorie van verwerking en onzekerheid.
‘Voeg één ding tegelijk toe’ betekent daarom niet dat iedere property afzonderlijk moet worden geïmplementeerd. Twee eenvoudige scalars die op dezelfde manier worden gelezen, gevalideerd en opgeslagen, kunnen prima tegelijk worden toegevoegd. Een scalar en een foreign key horen doorgaans niet in dezelfde stap thuis, omdat zij fundamenteel anders kunnen falen.
Thin Wire probeert geen piepkleine commits af te dwingen. De methode probeert te voorkomen dat verschillende probleemklassen gelijktijdig worden geïntroduceerd.
De opbrengst moet nuchter worden geformuleerd. Het is verleidelijk om te stellen dat het gelijktijdig wijzigen van N zaken een foutruimte van 2ᴺ oplevert, maar als algemene stelling is dat te sterk.
De bescheidener en beter verdedigbare formulering luidt:
Naarmate meer onafhankelijke variabelen tegelijk veranderen, groeit het aantal mogelijke oorzaken en interacties snel.
Wanneer het systeem niet meer werkt nadat één nieuwe faalmodusklasse is toegevoegd, blijft de lijst met verdachten kort.
Dat is de werkelijke winst. Fouten verdwijnen niet, maar het wordt veel eenvoudiger om te zien welk probleem er daadwerkelijk voorligt.
3. Klaar betekent dat het resultaat in de bestemming bestaat
Succes wordt vastgesteld aan de ontvangende kant, niet aan de verzendende kant.
Niet:
- de API retourneerde
200 OK; - het bericht is gepubliceerd;
- de job is zonder exception afgerond;
- de logging meldt succes.
Maar:
De bedoelde toestand bestaat aantoonbaar in de bestemming.
Bij een database-integratie kan het acceptatiecriterium soms niet ingewikkelder zijn dan een SELECT.
Dat klinkt misschien banaal, maar gedistribueerde systemen zitten vol schijnsuccessen. Een producer kan correct hebben gepubliceerd, een queue kan correct hebben afgeleverd en een consumer kan correct hebben gelezen, terwijl de databasetransactie alsnog is teruggedraaid, slechts gedeeltelijk is uitgevoerd of in de verkeerde tabel is terechtgekomen.
Iedere tussenliggende component kan naar waarheid melden dat zijn taak is afgerond, terwijl het bedoelde bedrijfsresultaat nog steeds ontbreekt.
Daarom blijft de uitkomst in de bestemming het vaste referentiepunt.
4. Bewuste schuld is aanvaardbaar; verborgen schuld niet
Bij de eerste draad worden doorgaans verschillende zaken uitgesteld:
- volledige validatie;
- authenticatie en autorisatie;
- retries;
- dead-letterafhandeling;
- de tegenrichting;
- performance-optimalisatie.
Dat is niet automatisch onzorgvuldig, zolang iedere uitsluiting expliciet is en vastligt op welk moment zij wordt afgehandeld.
Zonder die registratie is tijdelijke eenvoud niet te onderscheiden van vergeten werk.
Een eerste draad kan bijvoorbeeld zonder retries worden gebouwd, zolang voor iedereen duidelijk is dat die vóór productiegebruik moeten worden toegevoegd.
De schuld is dan geen toevallig bijproduct, maar onderdeel van het bouwplan.
Begin klein, maar niet betekenisloos klein
De eerste draad hoeft niet het meest voorkomende scenario te vertegenwoordigen.
Het meest voorkomende scenario bevat vaak juist de meeste velden, uitzonderingen en varianten, waardoor meerdere onzekerheden tegelijk worden geïntroduceerd.
Het eenvoudigste geval is doorgaans een beter vertrekpunt, maar alleen wanneer het nog steeds een representatieve end-to-endroute aantoont.
Een te kunstmatig voorbeeld kan technisch slagen zonder dat het iets bruikbaars leert over de integratie.
De betere regel luidt daarom niet:
Kies altijd het eenvoudigste geval.
Maar:
Kies het eenvoudigste geval dat nog steeds een echte route, een echte transformatie en een echte landing bevat.
Dun zijn vereist ook subtractie.
De vraag is niet alleen welke informatie minimaal de rivier over moet, maar ook welke informatie niet hoeft te worden overgebracht omdat de bestemming haar zelf betrouwbaar kan afleiden.
Stel dat een client zowel een basiscode als verschillende daarvan afgeleide classificaties meestuurt. Op het eerste gezicht lijkt dat veiliger: er wordt meer informatie aangeleverd en er is minder serverlogica nodig.
Maar iedere afgeleide waarde introduceert een tweede versie van de waarheid.
Zodra de basiscode en een daarvan afgeleide classificatie niet langer overeenkomen, moet iemand beslissen:
- welke waarde voorrang heeft;
- welk systeem eigenaar is van de regel;
- waar de afleiding moet plaatsvinden;
- hoe inconsistenties worden opgelost.
Een dunne draad is daarom niet alleen klein wat betreft het aantal properties. Zij moet ook zo weinig mogelijk onafhankelijke waarheden vervoeren.
Wanneer je moet stoppen met kruipen
Weten of de methode bij je situatie past is één ding. Weten wanneer je haar moet ontgroeien is minstens zo belangrijk.
Wanneer een team na twintig stappen nog steeds ieder afzonderlijk veld als aparte draad behandelt, is de methode een ritueel geworden.
Kleine stappen zijn een middel om onbekende faalmodi te ontdekken. Zij zijn niet bedoeld als permanente snelheidsbeperking.
Na enkele representatieve draden moeten patronen zichtbaar worden:
- welke scalars zonder problemen worden overgebracht;
- hoe lookups falen;
- waar lengtebeperkingen problemen veroorzaken;
- waar nullability relevant wordt;
- welke transformaties hetzelfde patroon volgen.
Dat is het moment waarop de omvang van de stappen moet worden vergroot.
De bekende ‘regel van drie’ is hierbij een bruikbare heuristiek, geen statistische garantie. Wanneer na enkele draden nog geen duidelijke patronen ontstaan, is het systeem mogelijk minder homogeen dan verwacht en blijft een kleinere stapgrootte gerechtvaardigd.
Het werkelijke anti-patroon: droogzwemmen
Het tegenovergestelde van Thin Wire is niet zorgvuldig ontwerpen.
Het is langdurig ontwerpen zonder operationele feedback.
Er wordt een volledig contract geschreven. Iedere denkbare uitzondering wordt vooraf bedacht. Pas daarna wordt de echte bron aangesloten, waarna blijkt dat de bron:
- velden anders vult dan verwacht;
- nullwaarden aanlevert waar die niet werden voorzien;
- verouderde records verstuurt;
- arrays gebruikt waar één waarde werd verwacht;
- half-afgeronde processen zichtbaar maakt;
- waarden produceert die niet binnen het doelschema passen.
Het contract wordt aangepast, maar de interface werkt nog steeds niet.
Dat voelt productief, omdat documenten, mappings en regels zich blijven opstapelen. Toch blijft het droogzwemmen zolang er nog geen enkel record daadwerkelijk de rivier is overgestoken.
Dat betekent niet dat ontwerp onbelangrijk is. Integendeel.
Het onderscheid zit in wie op dat ontwerp wacht. Ontwerp je voor een landing die je zelf beheert, dan is maandenlang specificeren zonder één werkend record inderdaad droogzwemmen. Ontwerp je een koppelvlak waarop een andere partij haar eigen planning baseert, dan is datzelfde specificeren geen uitstel maar levering: zonder complete specificatie kan zij niet beginnen.
Een geversioneerde spec, een mock en contracttesten zijn daar geen ceremonie. Zij vormen het minimum waarmee twee partijen parallel kunnen werken.
Droogzwemmen is ontwerpen zonder enige feedback. Een contract dat samen met de andere partij wordt vastgelegd, krijgt die feedback wel — alleen niet van draaiende code, maar van degene die ermee moet werken.
Een bruikbare landing, compleet met constraints en een contractspecificatie, vraagt om aanzienlijk ontwerpwerk. In veel gevallen vindt het grootste deel van de inspanning al vóór de eerste draad plaats:
- het API-contract;
- de mapping;
- de doeltabellen;
- de foreign keys;
- de basisvalidatie;
- de keuze van het bronsysteem.
De regel is eenvoudig:
Ontwerp genoeg om een echte landing te realiseren en laat die werkende landing vervolgens het verdere ontwerp informeren.
Loopt de interface over een organisatiegrens, dan verschuift die regel: ontwerp genoeg om de ander te laten beginnen, en laat je eigen werkende landing daarna nog steeds het verdere ontwerp bijstellen.
De import als oervorm
De methode wordt waarschijnlijk het duidelijkst in een klassieke importcasus.
Gegevens uit een extern product moeten worden overgebracht naar een intern datamodel. De bestemming ligt vast, maar de brondata blijkt vervuild en inconsistent.
De verleiding is om eerst de volledige mapping af te ronden en daarna alles in één importgang te verwerken.
De Thin Wire-benadering draait die volgorde om.
Eerst worden alle records uitsluitend met de minimale sleutel ingevoegd en wordt gecontroleerd of de rijen bestaan.
Daarna:
- wordt één categorie velden bijgewerkt;
- worden de ontstane fouten geanalyseerd;
- wordt een expliciet besluit genomen;
- wordt het proces met de volgende categorie herhaald.
Stel dat een naamveld wordt toegevoegd en elf waarden langer blijken te zijn dan de doelkolom toestaat.
Dat is nu één duidelijk afgebakend probleem waarvoor één expliciet besluit nodig is:
- de waarden afkappen;
- de kolom verbreden;
- de records afwijzen;
- een waarschuwing vastleggen;
- eisen dat de brondata wordt gecorrigeerd.
Pas daarna volgt de volgende kolom of faalmodusklasse.
De import blijft set-georiënteerd, maar de redenering blijft ordelijk: één probleemklasse tegelijk.
De kracht zit niet in INSERT en UPDATE op zichzelf, maar in de bouwvolgorde:
- eerst bestaan de rijen;
- daarna groeit hun inhoud;
- iedere gang introduceert één nieuw soort onzekerheid;
- iedere foutcategorie krijgt een eigen besluit en eigen logging.
Dat is Thin Wire, ongeacht of iemand het die naam geeft.
Oude wijn in een bruikbare fles
Is Thin Wire een uniek idee?
Waarschijnlijk niet in fundamentele zin.
De samenstellende ideeën bestaan al lange tijd:
- incrementele ontwikkeling;
- end-to-endverificatie;
- vertical slices;
- tracer bullets;
- walking skeletons;
- set-georiënteerde ETL;
- één variabele tegelijk wijzigen.
Wat wel onderscheidend is, is de synthese:
- ‘dun’ verwijst naar de inhoudelijke omvang, niet naar een kunstmatige landing;
- succes wordt in de bestemming vastgesteld;
- uitbreidingen worden gescheiden per faalmodusklasse;
- de eerste route is klein maar echt;
- tijdelijke schuld wordt expliciet geregistreerd;
- de draad vervoert zo weinig mogelijk afgeleide waarheden;
- de methode bevat een bewuste overgang naar grotere stappen;
- zij vraagt aan de overkant zeggenschap, een af contract of een korte iteratie.
Dat maakt het geen wetenschappelijke doorbraak, en dat hoeft ook niet.
Veel bruikbare patronen zijn geen ontdekkingen van iets wat nooit eerder heeft bestaan. Zij geven een naam aan herhaalbare vakkennis, maken impliciete keuzes bespreekbaar en helpen een team om op het juiste moment hetzelfde soort besluit te nemen.
De bescheiden claim is daarom sterker dan de grootse:
Thin Wire is een methode voor het bepalen van de bouwvolgorde van een integratie. Zij houdt een groot einddoel beheersbaar door eerst één kleine, echte route aan te leggen en die daarna per faalmodusklasse uit te breiden.
Of, om terug te keren naar de olifant:
Thin Wire is geen nieuw antwoord op de vraag hoe je een olifant opeet. Het is een manier om bij integraties te bepalen waar je de eerste hap neemt, hoe je weet dat je hem daadwerkelijk hebt doorgeslikt en wanneer je klaar bent om grotere happen te nemen.