
I mine 14 års ingeniørerfaring så jeg mange mennesker træffe karrierebeslutninger baseret på muligheden for at arbejde på en helt ny tjeneste. Der er ikke noget galt med den beslutning. Men i dag vil vi fremsætte en selvmodsigende sag om at arbejde på kedelige migrationsprojekter. Hvad jeg ikke var klar over tidligt i min karriere, var, at det meste af min grundlæggende softwareudviklingslæring kom fra projekter, der var migreringsprojekter - fx migrering af et underliggende datalager til en anden cloud-baseret teknologi eller udfasning af en monolitisk tjeneste til fordel for nye mikrotjenester osv.
Dette skyldes, at migreringer i sagens natur er svære: du er tvunget til at opfylde, hvis ikke overstige, en eksisterende bar for tilgængelighed, skala, latency og kundeoplevelse, som er bygget og finpudset gennem årene af flere ingeniører. Du vil ikke møde disse begrænsninger på et helt nyt system, fordi du er fri til at definere dem. Ikke nok med det, uanset hvor grundig du er med migreringer, vil der være skjulte skeletter i skabet at håndtere, når du skifter over til nye dele af systemet (Se denne interessante artikel om, hvordan Doordashs migrering fra Int til BigInt for et databasefelt var fyldt med blokeringer).
Disse projekter tvinger dig til omhyggeligt at tænke over testmetoder, nøjagtigheden af resultater fra nye systemer, planer for softwareudrulning, planer om tilbagerulning af software osv., så du ikke altid stresser over at arbejde på et helt nyt system, fordi der ikke er nogen eksisterende kunder, som du betjener. Det mest kedelige er, at eksisterende kunder ikke skal vide, at du rent faktisk har udskiftet et underliggende system eller kodebase uden deres viden.
Jeg ser ofte nye ingeniører, der ønsker at prøve en ny teknologi og erstatte en eksisterende funktionalitet, eller nogen, der ønsker at lave en komplet refactor af kodebasen. Hvis dette er en indesluttet ændring (f.eks. at bruge et gennemtestet open source-bibliotek til at udføre en lille operation i tjenesten osv.), har jeg ikke noget imod det. Men hvis det er en større arkitektonisk ændring eller omarbejdelse af en hel kodebase, er det vigtigt at huske et berømt ingeniørprincip "Respect What Came Before." (Jeg fandt dette tweet sjovt, som refererer til legacy kode som legendarisk kode.)
Når vi vender tilbage til punktet med migreringsprojekter, er det altid klogt at vurdere, om du kan løse det samme problem med en forholdsvis mindre indsats i forhold til at udføre en større revision af kodebasen eller arkitekturen. Men appellen ved at bruge en ny teknologi eller designmønster er altid fristende, så hvordan vurderer vi denne beslutning? Her er et par spørgsmål og overvejelser, der hjælper dig med at komme i gang, før du begiver dig ud på en migrationsrejse:
Bliver virksomheden (eller kundeoplevelsen) påvirket negativt, eller vil den blive påvirket i fremtiden, hvis vi ikke løser dette problem, og har teamet udtømt alle mulighederne for at løse dette uden en større forpligtelse til et større migreringsprojekt? Vælg en anmeldelse fra en anden senioringeniør, som ikke er på dit hold, og som kan fungere som en djævelens advokat for at presse dit ræsonnement. Nogle eksempler på begrundelser kunne være at forbedre agiliteten med 4 udviklermåneder for hver funktionslancering, ved at bruge forskellige teknologiske stakke til forskellige tjenester for at forbedre p99-latenstiden med 400 ms, fjerne skaleringsflaskehalse ud over X TPS osv. Søg altid uenigheder for at bryde din bekræftelsesbias i sådanne situationer.
Sammenlign indsatsen for at udføre migreringen med de fordele, det vil give, så du kan estimere, hvor lang tid det vil tage at begynde at høste fordelene af projektet. Et personligt eksempel, som jeg kan dele, er som følger:
Mit team ejede to separate systemer, der betjener to forskellige sæt kundebaser, og hver lancering af nye funktioner krævede, at teamet lavede lignende, men ikke nøjagtige, ændringer til disse separate systemer. Samlet set førte duplikeringen til en ekstra indsats på 1 udvikler om måneden per funktion. Vi lancerede omkring 4 sådanne funktioner hvert år, hvilket førte til 4 udviklermåneder med duplikeret eller spildt indsats. Dette var frustrerende for ingeniører. En af ingeniørerne kom med et forslag om at kombinere disse to systemer og anslåede indsatsen til 24 udviklermåneder. Det ville tage 24 funktionslanceringer og 6 år (forudsat 4 funktioner lanceret om året) for teamet at begynde at høste fordelene af migreringen. Vi foretog ikke migreringen og gik over til en alternativ tilgang med at bruge delte biblioteker for at reducere duplikeringsindsatsen med 50 %, og senere udfasede vi systemet efter 3 år til fordel for en anden tjeneste.
I nogle tilfælde er migreringen en top-down vejledning for at nå et bredere mål (f.eks. Amazon skifter væk fra Oracle ), hvor du stadig kan udføre analysen, men ikke er forpligtet til at få godkendelse for at fortsætte med projektet.
Når du har identificeret de rigtige begrundelser for at foretage migreringen og presset ræsonnementet med nogle eksterne ingeniører eller ledere, er det tid til at gå videre til de næste trin.
Dette svarer til, hvad du ville gøre, mens du forbereder dig til et systemdesign-interview . Når funktionelle og ikke-funktionelle krav er fastlagt, er det klogt at glemme det eksisterende system indtil videre og lægge ud, hvordan du ville bygge et nyt system, hvis der ikke var nogen begrænsninger.
Grunden til at lave denne øvelse er, at mange eksisterende teammedlemmer vil have en ubevidst bias til at bygge et nyt system, der ikke er meget forskelligt fra det eksisterende system, hvilket i mange tilfælde overvinder selve formålet med migrering. Overvej et andet eksempel fra min fortid:
Inddragelse af en mere erfaren person, som ikke arbejdede på eksisterende systemer, styrede samtalen til at bygge et helt andet system, der var mere skalerbart, i realtid og lettere at vedligeholde. Dette er måske ikke altid muligt, men det skader ikke at prøve at gennemgå denne øvelse.
Hvis du laver en like-for-like-migrering, som vi foreslog før (dvs. at flytte en lokal SQL DB til en cloud SQL DB), har du måske lettere ved at opfylde ikke-funktionelle krav. Men hvis dit slutsystem er drastisk anderledes end det nuværende system, bør du i det mindste gøre et forsøg på at rette det indbyggede anti-mønster i systemet. For eksempel, i stedet for at spørge efter en opdateringsændring af en nøgle i databasen, kan du udgive en ændringsmeddelelse ved hjælp af en Pub/Sub-tjeneste til abonnenter.
Som ethvert projekt i distribuerede systemer vil migreringer dog have afvejninger, når det kommer til ikke-funktionelle krav, og du bliver nødt til at planlægge for det. Hvis der f.eks. er en monolittjeneste med en tilgængelighed på 99,9 %, der håndterer to separate forretningsrelaterede beregninger (estimat for leveringsdato og estimat for forsendelsesomkostninger), og vi beslutter at opdele dette ansvar i to mikrotjenester A (estimat for leveringsdato) og B (estimat for leveringsomkostninger), som hver har tilgængelighed af et system på 99 %, bliver samlet set: 9 %.
P(A) * P (B) = 0,999 * 0,999 = 99,8 % tilgængelighed
Oprettelse af mikrotjenester fra en monolit førte til en reduktion af tilgængeligheden fra 99,9 % til 99,8 %.
Husk altid, at hvis du har brug for resultater fra 'n' servicekald (sekventielle eller parallelle servicekald) for at returnere et svar til din klient, multiplicerer du den individuelle tilgængelighed af hver af 'n' tjenester for at nå frem til den endelige tilgængelighed af systemet.
For at opfylde eller overgå den oprindelige tilgængelighed af systemet (dvs. 99,9%), bliver vi nødt til at tænke på andre teknikker som caching, genforsøg osv. Men hver af disse muligheder har sine egne ulemper. For eksempel kan caching i nogle tilfælde betyde, at dit system skal være i stand til at tolerere forældede data; genforsøg kan tilføje forsinkelser og gøre systemet modtageligt for genforsøg med storme osv.
Hvis du udfører denne øvelse, bør du dog kunne se, om du i det mindste opfylder en eksisterende bar på ikke-funktionelle krav, eller om du har brug for ledelsesgodkendelse af nye ikke-funktionelle krav, som du ønsker at stille til dine kunder.
Med et nyt system vil dine kunder tage din nye klientversion til sig. Med migreringsprojekter skal du muligvis håndtere problemet med, hvad hvis alle kunder ikke kan migrere til den nye version af klienten (dvs. tænker på bagudkompatibilitet). Hvis alle dine kunder er interne i virksomheden, eller du har begrænset adoption uden for virksomheden, kan du arbejde sammen med alle dine kunder om at flytte dem til en ny version af kunden.
I andre tilfælde er dette simpelthen ikke muligt. For eksempel, hvis du ejer en stor cloud-tjeneste, der er bredt udbredt i branchen, er der ingen måde, du kan tvinge alle kunder til at flytte til en ny version af klienten. Dette kan tilføje betydelige blokeringer såvel som vedligeholdelsesomkostninger for teamet, og i nogle tilfælde er løsningen at vedligeholde to versioner af systemer, hvor det ældre system er i vedligeholdelsestilstand (dvs. ingen nye kunder tilføjes til dette system), og du giver et incitament til ældre kunder til at flytte til en nyere version af systemet, da det har forbedrede fordele for kunderne.
Men hvis du har en situation som det link, jeg delte ovenfor med Doordash, hvor brugen af Int som datatypen for primær nøgle ville løbe over, har du ingen anden mulighed end at tvinge alle til at udføre migreringen.
Når de bygger nye systemer, gør de fleste ingeniører et fantastisk stykke arbejde med at dække næsten alle brugssager. Det omvendte sker dog med migreringer, fordi du håndterer et system, der er udviklet, patchet og vedligeholdt af ti, hvis ikke hundredvis, af ingeniører før dig. Selvom du ønsker at lære om hver brugssag, kodesti eller systemflaskehals, er det svært at få hovedet viklet rundt om hele tjenesten.
I sådanne tilfælde er den enkleste ting at gøre at opsøge erfaringer fra teams, senioringeniører osv., som har udført lignende migreringer omkring, hvilke processer du kan følge for at dække over dine blinde vinkler. Mange virksomheder følger en proces med bredere design- og migreringsgennemgange på organisationsniveau. Søg uenigheder som en hellig del af processen for at styrke din tilgang og forståelse. Migrationer er fyldt med landminer, der snubler på uventede måder.
De fleste migrationer er normalt i en af de to kategorier nedenfor eller en kombination af begge:
Tjenestemigreringer: Udfasning af en eksisterende tjeneste til fordel for en ny arkitektur, som kan bestå i at bruge dele af den nuværende tjeneste og en ny tjeneste eller lancere nye mikrotjenester til at erstatte et eksisterende system.
Datastore migrationer: Udfasning af et eksisterende datalager og erstatning af det med et nyt datalager eller brug af et hændelsesdrevet system.
Selvom du ikke kan finde et eksakt migreringseksempel, kan du altid tegne bredere læringer for disse buckets. I min personlige erfaring var datalagermigreringer de sværeste, da der er bekymringer omkring nøjagtigheden af data, som påvirkes på grund af sammenhængsproblemer mellem gamle og nye datalagre. For eksempel kan en bruger se en ældre version af data fra et nyt datalager på grund af forsinkelser i udbredelsen.
At køre eksisterende og nye systemer parallelt, mens du kun serverer data fra det eksisterende system, giver dig mulighed for at sammenligne resultaterne af begge systemer med reelle kundeønsker. Dette er det mest nyttige og effektive trin til at sammenligne og validere, at dit nye system fungerer korrekt.
For mange år tilbage arbejdede jeg på en tjenestemigrering til en ny teknisk stak. Når vores gamle tjeneste modtog kundeforespørgsler, lavede vi et parallelt asynkront opkald til vores nye tjeneste i backend. Vi vil logge eksisterende og nye serviceresultater til en S3-lokation. Vi kørte derefter en AWS Athena-forespørgsel sidst på dagen for at finde ud af eventuelle uoverensstemmelser og identificere eventuelle problemer med den nye tjeneste. Det var stadig noget af en forudsigelig ting at gøre sammenlignet med en anden vanskelig migration, vi håndterede med et datalager.
Vi flyttede et gammelt SQL-datalager til et nyt NoSQL- datalager, der var udfyldt fra en mere pålidelig og ny datakilde. Men tidspunktet, hvor specifikke nøgler blev opdateret mellem gamle og nye datalagre, var uforudsigeligt, da de kom fra to helt forskellige systemer.
Efter uden held at have prøvet flere metoder til at sammenligne data mellem gamle og nye datalagre, arbejdede vi sammen med vores upstream-teams for at frigive versioner til datanøgler, så vi kunne udføre datanøjagtighedssammenligning for en given nøgle ved hjælp af versioner mellem begge systemer. Dette var engangsarbejde, da vi ikke havde brug for versioner efter projektet, men der var ingen anden måde at håndtere dette på.
Selv efter at have kørt Trin 5, hvor du var i stand til nøjagtigt at sammenligne resultaterne af det gamle og nye system grundigt, er det meget muligt, at du aldrig rammer en bestemt type anmodning fra nogle få kunder, som sjældent bruger dit system. Jeg har mistet søvnen, mens jeg arbejdede på nogle af disse migreringsprojekter og tænkte: "Hvad nu hvis alt går galt med det nye system?"
Den nemmeste måde at tackle dette på var at have et slukket skifte til det nye system, hvis vores alarmer fangede noget uventet, eller vi manuelt udløste det for at flytte trafikken tilbage til det gamle system. Husk, det er ikke så nemt, som det lyder. I nogle tilfælde er der måske ikke en måde at flytte til et ældre system på, men at have denne håndtag vil aflaste en masse pres på holdet.
I tilfælde, hvor dette ikke er muligt, er dit eneste punkt at stole på at være grundig med trin 5. (Køre gamle og nye systemer parallelt). Dette efterfølges af en langsom gradvis udrulning af det nye system. Du kan definere en langsom gradvis udrulning ved hjælp af teknikker som at flytte en lille procentdel (1 % efterfulgt af 5 %, 10 %, 25 %, 50 %, 100 %) af trafikken, der skal betjenes af en ny tjeneste, eller håndplukke nogle få kunder, der skal betjenes af en ny tjeneste, som du arbejder tæt sammen med under migreringen osv.
Det er også vigtigt at gennemgå en hændelsesrapport, som skal følges af operatører, hvis tingene går galt. Hvis alt fejler, kan manuel indgriben hjælpe med kantsager, der blev savnet, men dette kan hurtigt blive uoverskueligt, hvis antallet af berørte kunder vokser til tusindvis. Dette er grunden til at give tilstrækkelig tid til faserne beskrevet i punkt 5 og 6.
Selvom arbejdet med migreringer ikke er den eneste måde at finpudse nogle af disse færdigheder på, kan det helt sikkert fremskynde din læring, som du kan anvende på dine fremtidige projekter, selvom det betyder, at de er helt nye initiativer. Migrationsprojekter er mindre glamourøse, men var dem, der gjorde mig kamptestet, især når jeg giver feedback på designdokumenter eller andre tekniske dokumenter.
Så hvis du får en chance for at arbejde på en, så prøv det: du vil ikke blive skuffet og vil have en karrierelang læring, som du forhåbentlig vil give videre til andre for at bygge nogle modstandsdygtige systemer .