
Sa më shpejt të jetë procesi i zhvillimit, aq më shpejt zhvillohet kompania teknologjike.
FatkeqĂ«sisht, aplikacionet moderne punojnĂ« pĂ«r tĂ« na penguar â sistemet tona duhet tĂ« pĂ«rditĂ«sohen nĂ« kohĂ« reale dhe nĂ« tĂ« njĂ«jtĂ«n kohĂ« tĂ« mos ndihmojnĂ« askĂ«nd dhe tĂ« mos çojnĂ« nĂ« pezullime dhe ndĂ«rprerje. Zbatimi nĂ« kĂ«to sisteme bĂ«het njĂ« detyrĂ« e komplikuar dhe kĂ«rkon pipeline tĂ« ndĂ«rlikuara tĂ« furnizimit tĂ« vazhdueshĂ«m, madje edhe nĂ« ekipet e vogla.
Këto pipeline zakonisht kanë aplikime të ngushta, punojnë ngadalë dhe nuk janë të besueshme. Zhvilluesit duhet së pari t'i krijojnë ato manualisht, pastaj t'i menaxhojnë, dhe kompanitë shpesh angazhojnë ekipe të tëra DevOps për këtë.
ShpejtĂ«sia e kĂ«tyre pipeline-ve pĂ«rcakton shpejtĂ«sinĂ« e zhvillimit. NĂ« ekipet mĂ« tĂ« mira, zbatimi zgjat 5â10 minuta, por zakonisht gjithçka bĂ«het shumĂ« mĂ« gjatĂ«, dhe pĂ«r njĂ« zbatim janĂ« tĂ« nevojshme disa orĂ«.
NĂ« Dark, kjo zgjat 50 ms. PesĂ«dhjetĂ«. Milisekonda. Dark â , e krijuar veçanĂ«risht pĂ«r dĂ«rgesa tĂ« vazhdueshme, dhe tĂ« gjitha aspektet e Dark, pĂ«rfshirĂ« gjuhĂ«n vetĂ«, janĂ« ndĂ«rtuar me shikim pĂ«r njĂ« vendosje tĂ« sigurt dhe tĂ« menjĂ«hershme.
Pse janë ngadalë linjat e dërgesës së vazhdueshme?
Supozoni se kemi një aplikacion web Python dhe tashmë kemi krijuar një proces të mahnitshëm dhe modern të dërgesës së vazhdueshme. Për një zhvillues që është i angazhuar çdo ditë me këtë projekt, vendosja e një ndryshimi të vogël do të dukej kështu:
Bërja e ndryshimeve
- Krijimi i një dege të re në git
- Bërja e ndryshimeve pas kalimit të funksionit
- Testimi modular për të verifikuar ndryshimet me dhe pa kalim të funksionit
Kërkesa për bashkimin
- Angazhimi i ndryshimeve
- Dërgimi i ndryshimeve në depo të largët në github
- Kërkesa për bashkimin
- Ndërtimi i CI bëhet automatikisht në sfond
- Rishikimi i kodit
- Disa rishikime të tjera, nëse është e nevojshme
- Bashkimi i ndryshimeve me masterin git.
CI kryhet në master
- Instalimi i varësive frontend përmes npm
- Ndërtimi dhe optimizimi i burimeve HTML+CSS+JS
- Përvedhja e testeve modulare dhe funksionale në frontend
- Instalimi i varësive Python nga PyPI
- Përvedhja e testeve modulare dhe funksionale në backend
- Testimi i integrimit në të dy anët
- Dërgimi i burimeve të frontend në CDN
- Ndërtimi i kontejnerit për programin Python
- Dërgimi i kontejnerit në regjistrin
- Përditësimi i manifestit Kubernetes
Zëvendësimi i kodit të vjetër me të riun
- Kubernetes nget disa instanca të kontejnerit të ri
- Kubernetes pret që instancat të bëhen funksionale
- Kubernetes shton instancat në balancuesin e ngarkesës HTTP
- Kubernetes pret që instancat e vjetra të mos përdoren më
- Kubernetes ndalon instancat e vjetra
- Kubernetes përsërit këto operacione derisa instancat e reja të zëvendësojnë të gjitha të vjetrat
Aktivizimi i kalimtarit të ri të funksionit
- Kodi i ri aktivizohet vetëm për veten, për të siguruar që gjithçka është në rregull
- Kodi i ri aktivizohet për 10% të përdoruesve, maten metrike operacionale dhe të biznesit
- Kodi i ri aktivizohet për 50% të përdoruesve, maten metrike operacionale dhe të biznesit
- Kodi i ri aktivizohet për 100% të përdoruesve, maten metrike operacionale dhe të biznesit
- Më në fund, përsërisni të gjithë procedurën për të hequr kodin e vjetër dhe kalimtarin
Procesi varet nga mjetet, gjuha dhe përdorimi i arkitekturave të orientuara nga shërbimet, por në mënyrë të përgjithshme duket kështu. Nuk e përmenda implementimin me migrimin e bazave të të dhënave, sepse kjo kërkon planifikim të kujdesshëm, por më poshtë do të flas si e menaxhon këtë Dark.
Ka shumë komponentë këtu, dhe shumë prej tyre mund të ngadalësojnë, dështojnë, shkaktojnë konkurencë përkohshme ose të arrijnë sistemin operativ në kolaps.
Dhe për shkak se këto pipeline pothuajse gjithmonë krijohen për raste të veçanta, është e vështirë të baza mbi to. Ka ditë kur nuk mund të implementosh kodin sepse në Dockerfile ka probleme, ka pasur një dështim në një nga dhjetëra shërbimet apo specialisti i nevojshëm është në pushim.
Më keq akoma, shumë nga këto hapa nuk bëjnë asgjë të dobishme. Ata ishin të nevojshëm më parë, kur implementonim kodin menjëherë për përdoruesit, por tani kemi të ndërprera për kodin e ri, dhe këto procese janë ndarë. Si pasojë, hapi kur implementohet kodi (i vjetër zëvendësohet me të riun) tani është bërë thjesht një rrezik i panevojshëm.
Sigurisht, kjo është një pipeline shumë e menduar. Ekipa që e krijoi atë, nuk kurseu kohë dhe para për ta çuar atë në veprim sa më shpejt. Në përgjithësi, pipeline-t e zhvillimit janë shumë më të ngadalta dhe më pak të besueshme.
Implementimi i dorëzimit të vazhdueshëm në Dark
Dorëzimi i vazhdueshëm është kaq i rëndësishëm për Dark, sa që që në fillim vendosëm të arrijmë një kohë më të shkurtër se një sekondë. Ne shqyrtuam të gjitha hapat e pipeline-it për të hequr çdo të tepërt dhe i arritëm të tjerët deri në perfekcion. Këtu është se si i hoqëm hapat.
Jessie Frazelle () shpiku fjalën e re deployless (pa nevojë për zhvillim) në konferencën Future of Software Development në Reikjavik
Ne menjëherë vendosëm që Dark do të bazohej në konceptin «deployless» (faleminderit për neologjizmin). Deployless do të thotë që çdo kod është menjëherë i vendosur dhe i gatshëm për t'u përdorur në prodhim. Sigurisht, ne nuk do të kalojmë kodin e prishur ose të papërfunduar (principet e sigurisë do i përshkruaj më poshtë).
GjatĂ« demonstrimit tĂ« Dark, shpesh na pyesnin se si arritĂ«m tĂ« shpejtonim kaq shumĂ« vendosjen. ĂshtĂ« njĂ« pyetje e çuditshme. NjerĂ«zit mendojnĂ« ndoshta se ne kemi shpikur ndonjĂ« superteknologji qĂ« krahasohet me kodin, e kompilon atĂ«, e paketĂ«son nĂ« njĂ« kontejner, aktivizon njĂ« makinĂ« virtuale, dhe nis kontejnerin nĂ« mĂ«nyrĂ« tĂ« ftohtĂ« â dhe gjithĂ« kjo brenda 50 ms. E pamundur, ndoshta. Por ne krijuam njĂ« motor tĂ« posaçëm vendosjeje qĂ« nuk ka nevojĂ« pĂ«r gjithçka kĂ«tĂ«.
Dark aktivizon interpretuesit nĂ« cloud. Supozoni se shkruani kod nĂ« njĂ« funksion ose trajtesĂ« HTTP ose ngjarjesh. Ne dĂ«rgojmĂ« diferencĂ«n nĂ« pemĂ«n sintaksore abstracte (implementimi i kodit qĂ« e pĂ«rdor redaktori ynĂ« dhe serverĂ«t) nĂ« serverĂ«t tanĂ«, dhe pastaj e aktivizojmĂ« kĂ«tĂ« kod kur merrni kĂ«rkesa. KĂ«shtu vendosja duket thjesht si njĂ« regjistrim modest nĂ« bazĂ«n e tĂ« dhĂ«nave â momentale dhe elementare. Vendosja ndodh kaq shpejt, sepse pĂ«rfshin tĂ« paktĂ«n tĂ« domosdoshme.
Në të ardhmen, ne planifikojmë të bëjmë Dark një kompilator infrastrukture, i cili do të krijojë dhe ekzekutojë infrastruktura ideale për performancë dhe besueshmëri të lartë të aplikacioneve. Menjëherë shpërndarja, sigurisht, nuk do të zhduket.
Shpërndarje e sigurt
Redaktori i strukturuar
Kodi në Dark shkruhet në redaktorin Dark. Redaktori i strukturuar nuk lejon gabime sintaksore. Në thelb, në Dark nuk ka as një analizues. Ndërsa ju shkruani tekstin, ne punojmë drejtpërdrejt me pemën abstrakte sintaksore (AST), si , , , dhe .
Ădo kod i papĂ«rfunduar nĂ« Dark ka semantikĂ« tĂ« lejuar ekzekutimi, pĂ«r tĂ« njĂ«jtĂ«n mĂ«nyrĂ« si . PĂ«r shembull, nĂ«se ju ndryshoni thirrjen e njĂ« funksioni, ne ruajmĂ« funksionin e vjetĂ«r derisa i ri tĂ« bĂ«het i pĂ«rshtatshĂ«m.
Ădo program nĂ« Dark ka kuptimin e tij, prandaj kodi i papĂ«rfunduar nuk e pengon tĂ« pĂ«rfunduarin tĂ« funksionojĂ«.
ReĆŸime redaktimi
Ju vendosni kodin në Dark në dy raste. E para: po shkruani një kod të ri dhe jeni përdoruesi i vetëm. Për shembull, ndodhet në REPL dhe përdoruesit e tjerë kurrë nuk do të kenë qasje në të, ose është një rrugë e re HTTP për të cilën nuk bëni referencë askund. Në këtë rast, mund të punoni pa ndonjë masë të kujdesit, dhe tani jeni duke punuar në ambientin e zhvillimit.
Situata e dytë: kodi tashmë është në përdorim. Nëse përmes kodit kalon trafik (funksione, menaxherë ngjarjesh, baza të dhënash, etj.), duhet të jeni të kujdesshëm. Për këtë, bllokojmë gjithë kodin në përdorim dhe kërkojmë të përdoren mjete më strukturore për redaktimin e tij. Në lidhje me mjetet strukturore do të flas më poshtë: kaluesit e funksioneve për menaxherët HTTP dhe ngjarjeve, një platformë e fuqishme migrimi për bazat e të dhënave dhe një metodë e re për menaxhimin e versioneve për funksione dhe lloje.
Kaluesit e funksioneve
NjĂ« nga mĂ«nyrat NĂ« Dark â zgjidhni disa probleme me njĂ« zgjidhje. NdĂ«rruesit e funksioneve kryejnĂ« shumĂ« detyra tĂ« ndryshme: zĂ«vendĂ«simi i ambientit vendas tĂ« zhvillimit, degĂ«t git, shpĂ«rndarja e kodit dhe, sigurisht, lĂ«shimi tradicional i ngadaltĂ« dhe tĂ« kontrolluar tĂ« kodit tĂ« ri.
Krijimi dhe shpërndarja e ndërruesit të funksionit bëhet në redaktorin tonë me një operacion. Ai krijon një hapësirë të bardhë për kodin e ri dhe ofron elemente kontrolli për kodin e vjetër dhe të ri, si edhe butona dhe komanda për kalimin gradual në kodin e ri ose për ta përjashtuar atë.
NdĂ«rruesit e funksioneve janĂ« tĂ« integruar nĂ« gjuhĂ«n Dark, dhe madje edhe ndĂ«rruesit nĂ« progres kryejnĂ« detyrĂ«n e tyre â nĂ«se kushti nĂ« ndĂ«rrues nuk pĂ«rmbushet, do tĂ« ekzekutohet kodi i vjetĂ«r i bllokuar.
Mjedisi i zhvillimit
Kalot e funksioneve zëvendësojnë ambientin lokal të zhvillimit. Sot, ekipet kanë të vështirë të sigurojnë që të gjithë të përdorin versione të njëjta të mjeteve dhe bibliotekave (mjetet e formatimit të kodit, linters, menaxherët e pacakëve, kompajlerët, preprocessors, mjetet e testimit, etj.) Me Dark, nuk është nevoja të instaloni varësi lokalisht, të menaxhoni instalimin lokal të Docker-it, ose të merrni masa të tjera për të siguruar ndonjë barazi midis ambientit të zhvillimit dhe prodhimit. , ne as që do të bëhemi pretendues se po përpiqemi për të.
Në vend që të krijoni një ambient lokal të klonuar, kalot në Dark krijojnë një sandëkut të ri në prodhim që zëvendëson ambientin e zhvillimit. Në të ardhmen, ne gjithashtu planifikojmë të krijojmë sandëku për pjesë të tjera të aplikacionit (p.sh., klonet momentale të bazës së të dhënave), megjithatë, për momentin, kjo nuk duket aq e rëndësishme.
Degët dhe shpërndarjet
Aktualisht ka disa mĂ«nyra pĂ«r tĂ« futur njĂ« kod tĂ« ri nĂ« sistemet: degĂ«t git, faza e implementimit dhe kaluesit e funksioneve. Ato zgjidhin njĂ« problem nĂ« pjesĂ« tĂ« ndryshme tĂ« procesit tĂ« punĂ«s: git â nĂ« fazat para implementimit, implementimi â nĂ« momentin e kalimit nga kodi i vjetĂ«r nĂ« atĂ« tĂ« ri, dhe kaluesit e funksioneve â pĂ«r njĂ« lĂ«shim tĂ« kontrolluar tĂ« kodit tĂ« ri.
MĂ«nyra mĂ« efikase â janĂ« kaluesit e funksioneve (nĂ« tĂ« njĂ«jtĂ«n kohĂ«, mĂ« tĂ« thjeshtĂ« pĂ«r t'u kuptuar dhe pĂ«rdorur). Me ta, mund tĂ« heqim plotĂ«sisht dy metodat e tjera. E veçanta Ă«shtĂ« se heqja e implementimit Ă«shtĂ« shumĂ« e dobishme â nĂ«se ne pĂ«rdorim gjithsesi kaluesit e funksioneve pĂ«r tĂ« aktivizuar kodin, atĂ«herĂ« hapi i kalimit tĂ« serverĂ«ve nĂ« kodin e ri krijon thjesht rreziqe tĂ« panevojshme.
Git është i vështirë për t'u përdorur, veçanërisht për ata që janë të rinj, dhe kjo e kufizon shumë, por ka degë të përshtatshme. Ne kemi zbutur shumë prej disavantazheve të git. Dark redaktohet në kohë reale dhe ofron mundësinë e bashkëpunimit në stilin e Google Docs, në mënyrë që të mos jetë e nevojshme të dërgosh kod dhe të mund të bësh më pak rebazime dhe bashkime.
Ndryshimet e funksioneve janë bazë e një implementimi të sigurt. Në kombinim me implementimet e menjëhershme, ato lejojnë testimin e shpejtë të koncepteve në segmente të vogla me rrezik të ulët, përveçse të aplikoni një ndryshim të madh që mund të rrëzojë sistemin.
Versionimi
Për ndryshimin e funksioneve dhe llojeve, përdorim versionimin. Nëse dëshiron të ndryshosh një funksion, Dark krijon një version të ri të këtij funksioni. Pastaj mund ta thërrasësh këtë version me anë të një ndërruesi në menaxherin HTTP ose ngjarjeve. (Nëse ky është një funksion i thellë në grafikun e thirrjeve, një version i ri krijohet për secilin funksion në rrugën. Mund të duket se është shumë, por funksionet nuk prishen nëse nuk i përdor, kështu që nuk do ta vësh re fare.)
Për këto arsye, ne gjithashtu versionojmë llojet. Kemi folur hollësisht për sistemin tonë të llojeve .
Me falënderon versionimi i funksioneve dhe tipeve, mund të bëni ndryshime në aplikacion gradualisht. Mund të kontrolloni që çdo menaxher i veçantë punon me versionin e ri, nuk është e nevojshme të bëni të gjitha ndryshimet në aplikacionet njëherësh (por ne kemi mjete për ta bërë këtë shpejt nëse dëshironi).
Kjo është shumë më e sigurt se sa të krijosh një implementim të plotë menjëherë, siç ndodh tani.
Versionet e reja të paketave dhe biblioteka standarde
Kur përditësoni një paketë në Dark, ne nuk zëvendësojmë menjëherë përdorimin e çdo funksioni ose tipi në tërë bazën e kodit. Kjo nuk është e sigurt. Kodi vazhdon të përdorë versionin e njëjtë që ka përdorur, ndërsa ju përditësoni përdorimin e funksioneve dhe tipeve në versionin e ri për çdo rast të veçantë me ndihmën e ndërfuturve.
Screenshot i një pjese të procesit automatik në Dark, që tregon dy versione të funksionit Dict::get. Dict::get_v0 ktheu tipin Any (nga i cili po heqim dorë), ndërsa Dict::get_v1 kthen tipin Option.
Ne ofrojmë shpesh një funksion të ri në bibliotekën standarde dhe përjashtojmë versionet e vjetra. Përdoruesit me versione të vjetra do të mbajnë akses në to, por përdoruesit e rinj nuk do të jenë në gjendje t'i përdorin. Ne jemi duke planifikuar të ofrojmë mjete për të kaluar përdoruesit nga versionet e vjetra në ato të reja me një hap, dhe përsëri me ndihmën e kaluesve të funksioneve.
Dark gjithashtu ofron mundësi unike: pasi ne ekzekutojmë kodin tuaj të punës, ne mund të testojmë vetë versionet e reja, duke krahasuar daljet për kërkesat e reja dhe ato të vjetra, për t'ju informuar mbi ndryshimet. Si rezultat, përditësimi i pakove, i cili shpesh bëhet në erë (ose kërkon testim të kujdesshëm për arsye sigurie), paraqet shumë më pak rreziqe dhe mund të ndodhi automatikisht.
Versionet e reja të Dark
Kalimi nga Python 2 në Python 3 zgjati një dekadë dhe ende mbetet një problem. Duke qenë se ne po krijojmë Dark për shpërndarje të vazhdueshme, duhet të kemi parasysh këto ndryshime të gjuhës.
Kur bëjmë ndryshime të vogla në gjuhë, krijojmë një Version të ri të Dark. Kodi i vjetër mbetet në versionin e vjetër të Dark, ndërsa kodi i ri përdoret në versionin e ri. Përdorimi i kaluesve ose versioneve të funksioneve mund të na ndihmojë për kalimin në versionin e ri të Dark.
Kjo është veçanërisht e dobishme, duke pasur parasysh se Dark ka dalë relativisht kohët e fundit. Shumë ndryshime në gjuhë ose bibliotekë mund të jenë të dështuara. Versionimi gradual i gjuhës na lejon të bëjmë përditësime të vogla, pra mund të mos jemi nxitim dhe të shtyjmë shumë vendime për gjuhën derisa të kemi më shumë përdorues dhe, për pasojë, më shumë informacione.
Migrimet e bazës së të dhënave
Për migrimin e sigurt të bazës së të dhënave, ekziston :
- Rikrijoni kodin për të mbështetur formate të reja dhe të vjetra
- Transformoni të gjitha të dhënat në formatin e ri
- Fshini qasjen e vjetër në të dhëna
Si rezultat, migrimi i bazës së të dhënave zgjat dhe kërkon shumë burime. Dhe kemi akumuluar skema të vjetruara, sepse edhe detyrat e thjeshta, siç është korrigjimi i emrit të tabelës ose kolonës, nuk ia vlen përpjekja e hapur.
Dark ka një platformë efikase të migrimit të bazës së të dhënave, e cila (shpresojmë) do ta thjeshtojë procesin aq shumë saqë do të ndaloni së frikësuari prej tij. Të gjitha depozitat e të dhënave në Dark (depozita çelë-shtesë ose tabela të përhershme me çelësa) kanë një lloj. Për të transferuar një depozitë të dhënash, ju thjesht i caktoni një lloj të ri dhe funksionin e rikthimit dhe restartrimit për të transformuar vlerat midis dy llojeve.
Qasja në depozitë të dhënash në Dark bëhet përmes emrave të ndryshueshëm të versioneve. Për shembull, depozita e dhënave Users fillimisht do të quhet Users-v0. Kur krijohet një version i ri me një lloj tjetër, emri ndryshon në Users-v1. Nëse të dhënat ruhen përmes Users-v0 dhe ju i qaseni ato përmes Users-v1, aplikohet funksioni i rikthimit. Nëse të dhënat ruhen përmes Users-v1 dhe ju i qaseni ato përmes Users-v0, aplikohet funksioni i restartrimit.
Ekrani i migrimit të bazës së të dhënave me emrat e fushave të bazës së të dhënave të vjetra, shprehjet e rikthimit dhe restartrimit dhe udhëzimet për aktivizimin e migrimit.
Përdorni ndërruesit e funksioneve për të drejtuar thirrjet nga Users-v0 në versionin Users-v1. Kjo mund të bëhet për një menaxher HTTP në një kohë, për të reduktuar rrezikun, dhe ndërruesit funksionojnë për përdorues të veçantë, në mënyrë që të mund të testoni se gjithçka funksionon siç pritej. Kur përdoruesit e Users-v0 të përfundojnë, Dark do të konvertojë të dhënat e mbetura nga formati i vjetër në të ri në sfond. Nuk do ta vëreni fare.
Testimi
Dark është dhe vlera të pandryshueshme, kështu që sipërfaqja e testimit është shumë më e vogël krahasuar me gjuhët objekt-orientuese me tipizim dinamik. Por edhe duhet testuar.
Në Dark, redaktori automatikisht ekzekuton testet modulare në sfond për kodin e redaktueshëm dhe për parazgjedhje ekzekuton këto teste për të gjithë ndërruesit e funksioneve. Në të ardhmen, ne duam që me ndihmën e tipeve statike të kryejmë automatikisht fuzzing të kodit për të gjetur defekte.
Për më tepër, Dark menaxhon infrastrukturën tuaj në prodhim, duke hapur mundësi të reja. Ne ruajmë automatikisht kërkesat HTTP në infrastrukturën Dark (për momentin ruajmë të gjitha kërkesat, por më vonë duam të kalojmë në tregime). Ne testojmë kodin e ri dhe realizojmë teste modulare, dhe nëse dëshironi, mund ta ktheni lehtësisht kërkesat interesante në teste modulare.
ĂfarĂ« heqim dorĂ«
Duke pasur parasysh se nuk kemi shpërndarje, por kemi ndërrues funksionesh, rreth 60% e pipeline-it të shpërndarjes mbetet jashtë. Nuk na nevojiten dega git ose pull-request-e, ndërtimi i resurseve backend dhe konteinerëve, dërgimi i resurseve dhe konteinerëve në regjistra ose hapat e shpërndarjes në Kubernetes.
Krahasimi i pipeline-it standard të dorëzimit të vazhdueshëm (majtas) dhe dorëzimi i vazhdueshëm i Dark (djathtas). Në Dark, dorëzimi përbëhet nga 6 hapa dhe një cikël, ndërsa versioni tradicional përfshin 35 hapa dhe 3 cikle.
Në Dark, implementimi përfundon me vetëm 6 hapa dhe 1 cikël (hapat që përsëriten disa herë), ndërsa një pipeline modern i furnizimit të vazhdueshëm përmban 35 hapa dhe 3 cikle. Në Dark, testet ekzekutohen automatikisht, dhe ju nuk e shihni këtë; varësitë instalohen automatikisht; gjithçka që lidhet me git ose Github nuk është më e nevojshme; ndërtimi, testimi dhe dërgimi i konteinerëve Docker nuk është i nevojshëm; implementimi në Kubernetes nuk është më i nevojshëm.
Edhe hapat e mbetur në Dark janë bërë më të thjeshtë. Pasi kalitësit e funksioneve mund të menaxhohen me një veprim, nuk është e nevojshme për të kaluar sërish nëpër të gjithë pipeline-in e implementimit për të hequr kodin e vjetër.
Ne e kemi thjeshtuar furnizimin e kodit sa më shumë që të mundemi, duke zvogëluar kohën dhe rreziqet e furnizimit të vazhdueshëm. Gjithashtu, ne e kemi thjeshtuar ndjeshëm azhurnimin e paketave, migrimin e bazave të të dhënave, testimin, menaxhimin e versioneve, instalimin e varësive, barazinë midis mjedisit të zhvillimit dhe prodhimit, si dhe përditësimet e shpejta dhe të sigurta të versioneve të gjuhës.
Unë po përgjigjem në pyetje rreth kësaj në .
Për të mësuar më shumë rreth mekanizmit të Dark, lexoni , (ose në ) ose . Nëse po shkoni në StrangeLoop në shtator, .
Burimi: habr.com
