Kanarialind on vĂ€ike lind, kes laulab pidevalt. Need linnud on metaani ja vingugaasi suhtes tundlikud. Isegi vĂ€ikese gaaside kontsentratsiooni korral Ă”hus kaotavad nad teadvuse vĂ”i surevad. Kulda otsijad ja kaevurid vĂ”tsid endaga linnud kaevandusse: kui kanarialind laulab, saab töötada, kui nad vaikivad â kaevanduses on gaas ja tuleb lahkuda. Kaevurid ohverdavad vĂ€ikese lauluva lindu, et eluga kaevandusest vĂ€lja pÀÀseda.

Sarnane praktika on levinud ka IT-sektoris. NĂ€iteks tavapĂ€rases ĂŒlesandes uue teenuse vĂ”i rakenduse versiooni tootmisse juurutamiseks koos eelneva testimisega. TestimisvĂ”imekus vĂ”ib olla liiga kallis, automatiseeritud testid ei kata kĂ”iki soovitud aspekte ja testimata jĂ€tmine ning kvaliteedi ohverdamine on riskantne. Just sellistes olukordades aitab kanaride juurutamise lĂ€henemine, kus natuke tĂ”elist tootmistegevust suunatakse uuele versioonile. LĂ€henemine aitab turvaliselt kontrollida uut versiooni tootmises, ohverdades natuke suurema eesmĂ€rgi nimel. LĂ€hemalt, kuidas see lĂ€henemine töötab, millist kasu see toob ja kuidas seda rakendada, rÀÀgib Andrei Markelov (), tuues nĂ€ite rakendamisest ettevĂ”ttes Infobip.
Andrei Markelov â juhtiv tarkvarainsener Infobipis, kellel on juba 11 aastat kogemust rakenduste arendamisel Java vĂ€ljakutes finanssektoris ja telekommunikatsioonis. Ta arendab avatud lĂ€htekoodiga tooteid, osaleb aktiivselt Atlassiani kogukonnas ja kirjutab pluginaid Atlassiani toodete jaoks. Prometheuse, Dockeri ja Redise evangelist.

EttevÔttest Infobip
See on globaalne telekommunikatsiooniplatvorm, mis vÔimaldab pankadel, jaekaubandusel, veebipoodidel ja transpordiettevÔtetel edastada sÔnumeid oma klientidele SMS-ide, push-teatete, e-kirjade ja hÀÀlteatega. Sellises Àritegevuses on oluline stabiilsus ja usaldusvÀÀrsus, et kliendid saaksid sÔnumid Ôigeaegselt.
Infobipi IT-infrastruktuur numbrites:
- 15 andmekeskust ĂŒle terve maailma;
- 500 ainulaadset teenust kasutuses;
- 2500 teenuse eksemplari, mis on palju rohkem kui meeskondi;
- 4,5 Tbyte kuus liiklust;
- 4,5 miljardit telefoninumbrit;
Ări kasvab ja koos sellega ka vĂ€ljaannete arv. Teeme ligikaudu 60 vĂ€ljaannet pĂ€evas, kuna kliendid soovivad rohkem vĂ”imalusi ja vĂ”imsust. Kuid see on keeruline â teenuseid on palju, aga meeskondi vĂ€he. Peame kiiresti kirjutama koodi, mis peab tootmises töötama ilma vigadeta.
VĂ€ljalaske
TĂŒĂŒpiline vĂ€ljaanne toimub meil nii. NĂ€iteks on meil teenused A, B, C, D ja E, millest igaĂŒht arendab eraldi meeskond.

MÔnes hetkes otsustab teenuse A meeskond rakendada uut versiooni, kuid teenuste B, C, D ja E meeskonnad ei tea sellest. Teenuse A meeskonna jaoks on kaks varianti.
Teeb inkrementaalset vĂ€ljaannet: esiteks asendab ĂŒhe versiooni ja siis teise.

Aga on teine variant: meeskond leidnud tÀiendavaid ressursse ja masineid, rakendab uue versiooni ja seejÀrel suunab ruuteri, ning versioon hakkab töötama tootmises.

Igatpidi, pĂ€rast rakendamist tekivad peaaegu alati probleemid, isegi kui versioon on testitud. Testida saab kĂ€sitsi, automatiseeritult vĂ”i mitte testida â probleemid tekivad igal juhul. KĂ”ige lihtsam ja Ă”igem viis neid lahendada on naasta töötavale versioonile. Alles pĂ€rast seda saab tegeleda kahjude ja pĂ”hjustega ning need parandada.
Nii et, mida me soovime?
Probleemid pole meile vajalikud. Kui kliendid avastavad need kiiremini kui meie, kahjustab see meie mainet. SeetÔttu peame leidma probleemid kiiremini kui kliendid.. Töötades ettepoole, minimaliseerime kahjud.
Samal ajal soovime me kiirendada rakendamist, et see toimuks kiiresti, lihtsalt, iseenesest ja ilma meeskonna stressita. Inseneride, DevOpsi inseneride ja programmide jaoks on oluline, et meeskonda hoida â uue versiooni vĂ€ljaanne on stressirohke. Meeskond ei ole kuluartikkel, meie eesmĂ€rk on mĂ”istlikult kasutada inimressursse..
Rakendamise probleemid
Kliendi liiklus on ettearvamatu. Ei ole vĂ”imalik ennustada, millal kliendi liiklus on minimaalne. Me ei tea, kus ja millal kliendid oma kampaaniaid kĂ€ivitavad â vĂ”ib-olla tĂ€na öösel Indias ja homme Hongkongis. Arvestades suurt ajavahet, ei taga isegi 2 tunni kohaliku rakendamise aeg, et kliendid ei kannata.
Teenusepakkujate probleemid. Messengrid ja teenusepakkujad on meie partnerid. MÔnikord esinevad neil tÔrked, mis pÔhjustavad uusi versioone rakendades vigu.
Jagatud meeskonnad. Meeskonnad, kes töötavad kliendi osa ja tagaplaani kallal, asuvad erinevates ajavööndites. SeetÔttu ei saa nad sageli omavahel kokku leppida.
Andmete keskuste kordamine arengutes. Ăhes andmekeskuses on 200 riidet, seda isegi ligikaudselt testida ei Ă”nnestu.
Aegidei ole lubatud! Meil on lubatud kĂ€ttesaadavuse tase (Error Budget), kui me töötame 99,99% ajast, nĂ€iteks, ja ĂŒlejÀÀnud protsendid on «veaĂ”igus». 100% usaldusvÀÀrsuse saavutamine ei ole vĂ”imalik, kuid on oluline pidevalt jĂ€lgida kokkuvarisemisi ja seise.
Klassikalised lahenduse variandid
Kirjutada koodi ilma vigadeta. Kui olin noor arendaja, tulid minu juurde juhid palvega teha vÀljaanne ilma vigadeta, kuid see ei ole alati vÔimalik.
Kirjutada teste. Testid töötavad, kuid mĂ”nikord mitte nii, nagu ettevĂ”te soovib. Raha teenimine ei ole testide ĂŒlesanne.
Testida etapis. Kolme ja poole aasta jooksul, mil ma töötasin Infobipis, ei ole ma kunagi nÀinud, et etapi staatus vÀhemalt osaliselt kattuks tootmisega.

Me proovisin isegi seda ideed arendada: esmalt meil oli etapp, siis eelprojekteerimine ja seejÀrel eelprojekteerimise eelprojekteerimine. Kuid isegi see ei aidanud - need ei kattunud isegi vÔimsusest. Etapi puhul suudame tagada baastegevuse, kuid ei tea, kuidas see töötab koormuste korral.
VĂ€ljalasu teeb see, kes arendas. See on hea praktika: isegi kui keegi muudab kommentaari nime, lisab ta kohe tootmisse. See aitab arendada vastutust ja mitte unustada tehtud muudatusi.
Lisaks on ka tÀiendavaid keerukusi. Arendajale on see stress - kulutada palju aega, et kÔik kÀsitsi kontrollida.
Kokkulepitud vÀljalasud. See variant pakub tavaliselt juhtkond: «Kokkulepe, et iga pÀev testite ja lisate uusi versioone». See ei toimi: alati on meeskond, kes ootab teisi vÔi vastupidi.
Smoke-testid
Veel ĂŒks vĂ”imalus meie juurutamisprobleemide lahendamiseks. Vaatame, kuidas smoke-testid töötavad eelneva nĂ€ite pĂ”hjal, kui meeskond A soovib juurutada uut versiooni.
Esiteks juurutab meeskond ĂŒhe instantsi tootmisse. SĂ”numite instantsi kaudu simuleeritakse reaalset liiklust, et see kattuks normaalse igapĂ€evase liiklusega. Kui kĂ”ik on korras, lĂŒlitab meeskond uue versiooni kasutajaliiklusele.

Teine variant - juurutada tĂ€iendava riistvaraga. Meeskond testib seda tootmises, siis lĂŒlitab selle sisse ja kĂ”ik töötab.

Smoke-testide puudused:
- Testidele ei saa usaldada. Kust saada sama liiklust nagu tootmises? VÔib kasutada eilset vÔi nÀdala vanust, kuid see ei pruugi alati vastata praegusele.
- Raskesti toetatav. Peab hoidma testkonto, pidevalt neid nullima enne iga juurutamist, kui salvestusse saadetakse aktiivsed kirjed. See on keerulisem kui kirjutada test oma liivakastis.
Ainus boonus on siin â saab kontrollida jĂ”udlust.
Canary-julitised
Smoke-testide puuduste tÔttu hakkasime kasutama canary-julitisi.
Praktika, sarnane sellele, kuidas kaevurid kasutasid kanareid gaasitaseme nĂ€itamiseks, on leidnud tee IT-sse. Me suuname veidi tĂ”elist tootmisliiklust uue versiooni peale, pĂŒĂŒdes samal ajal jÀÀda teenuse taseme kokkuleppe (SLA) piiridesse. SLA on meie âveaĂ”igusâ, mida saame kasutada kord aastas (vĂ”i mĂ”ne teise ajavahemiku jooksul). Kui kĂ”ik lĂ€heb hĂ€sti, lisame rohkem liiklust. Kui ei â taastame eelnevad versioonid.

Rakendamine ja nĂŒansid
Kuidas me rakendasime canary-julitisi? NÀiteks grupi kliendid saadavad sÔnumeid meie teenuse kaudu.

Juurutamine kĂ€ib nii: eemaldame ĂŒhe sĂ”lme koormustasakaalust (1), muudame versiooni (2) ja suuname eraldi veidi liiklust (3).

Ăldiselt on grupis kĂ”ik Ă”nnelikud, isegi kui ĂŒks kasutaja on rahulolematu. Kui kĂ”ik on hĂ€sti â muudame kĂ”ik versioonid.

NÀitan skeemiliselt, kuidas see enamasti mikroteenustes vÀlja nÀeb.
On teenuse leidmine ja kaks teenust: S1N1 ja S2. Esimene teenus (S1N1) teatab teenuse leidmisele, kui kÀivitub, ja teenuse leidmine salvestab selle. Teine teenus kahe sÔlmega (S2N1 ja S2N2) teatab samuti teenuse leidmisele kÀivitamisel.

Teine teenus esimese jaoks töötab serverina. Esimene kĂŒsib teenuse leidmiselt teavet oma serverite kohta, ja kui ta seda saab, otsib ja kontrollib neid (âtervise kontrollâ). Kui ta on kontrollinud, saadab ta neile sĂ”numeid.
Kui keegi tahab juurutada uut versiooni teisest teenusest, teatab ta teenuse leidmisele, et teine sÔlm saab olema canary-sÔlm: sinna suunatakse vÀhem liiklust, sest juurutamine toimub. Eemaldame canary-sÔlme koormustasakaalust ja esimene teenus ei suuna sinna liiklust.

Muudame versiooni ja Service Discovery teab, et teine sĂ”lm on nĂŒĂŒd canary â vĂ”ime anda sellele vĂ€hem koormust (5%). Kui kĂ”ik on hĂ€sti, vahetame versiooni, taastame koormuse ja töötame edasi.
Selleks, et kogu see teostada, vajame:
- tasakaalustamine;
- monitooring, kuna on oluline teada, mida iga kasutaja ootab ja kuidas meie teenused tÀpselt töötavad;
- versioonide analĂŒĂŒs, et mĂ”ista, kui hĂ€sti uus versioon tootmises töötada saab;
- automaatika â kirjutame ĂŒles seadistamise jĂ€rjekorra (deployment pipeline).

Tasakaalustamine
See on esimene asi, millele peaksime mÔtlema. On olemas kaks tasakaalustamise strateegiat.
Lihtsaim variant, kui ĂŒks sĂ”lm on alati canary. See sĂ”lm saab alati vĂ€hem liiklust ja alustame deployâd sellest. Probleemide korral vĂ”rdleme selle tööd enne ja ajal deployâd. NĂ€iteks, kui vigu on saanud kaks korda rohkem, siis on kahju kasvanud samuti kaks korda.
Canary-sÔlm mÀÀratakse deploy kÀigus. Kui deploy on lÔpule viidud ja me eemaldame selle canary-sÔlme staatuse, taastub liikluse tasakaal. VÀhemate masinate puhul saame ausa jaotuse.
JĂ€lgimine
Canary-releasesâi nurgakivi. Peame tĂ€pselt mĂ”istma, miks me seda teeme ja milliseid mÔÔdikuid soovime koguda.
MÔÔdikud, mida me oma teenustest kogume.
- Vigade arv, mis kirjutatakse logidesse. See on ilmne nĂ€itaja, et kĂ”ik töötab nii nagu peab. Ăldiselt on see hea mÔÔdik.
- PÀringute tÀitmise aeg (latency). Seda mÔÔdikut jÀlgitakse kÔikjal, kuna kÔik tahavad töötada kiiresti.
- JĂ€rjekorra suurus (throughput).
- Edukas vastuste arv sekundis.
- 95% kÔigist pÀringutest tÀidavad ettenÀhtud aega.
- ĂrimÔÔdikud: kui palju raha Ă€ri teenib teatud aja jooksul vĂ”i kasutajate loobumine. Need mÔÔdikud meie uue versiooni jaoks vĂ”ivad olla olulisemad kui need, mida insenerid lisavad.
MĂ”ned nĂ€ited mÔÔdikutest enamikes populaarsetes jĂ€lgimissĂŒsteemides.
Counter. See on teatud tÔusmine nÀiteks vigade arvu osas. Seda mÔÔdikut on lihtne interpoleerida ja graafikut uurida: eile oli 2 viga, aga tÀna 500, see tÀhendab, et midagi lÀks valesti.
Vigade arv minutis vĂ”i sekundis, see on kĂ”ige olulisem nĂ€itaja, mille saab arvutada kasutades Counterâit. Need andmed annavad selge ĂŒlevaate sĂŒsteemi toimimisest ajas. Vaatame nĂ€iteks vigade arvu graafikut sekundis kahe tootmissĂŒsteemi versiooni jaoks.

Esimeses versioonis oli vÀhe vigu, vÔib-olla ei töötanud audit. Teises versioonis on kÔik palju hullem. On selge, et esinevad probleemid, seega peame selle versiooni tagasivoolama.
Gauge. MÔÔdikud sarnanevad Counteriga, kuid me salvestame vÀÀrtusi, mis vÔivad nii suureneda kui ka vÀheneda. NÀiteks pÀringute tÀitmise aeg vÔi jÀrjekorra suurus.
Graafikul on nĂ€ide latentsusajast. Graafikult on nĂ€ha, et versioonid on sarnased, nendega on vĂ”imalik töötada. Kuid kui tĂ€helepanelikult vaadata, on mĂ€rgata, kuidas suurus muutub. Kui pĂ€ringute tĂ€itmise aeg suureneb koos kasutajate arvu suurenemisega, on kohe selge, et esinevad probleemid â varem seda ei olnud.

KokkuvĂ”te. Ăks kĂ”ige olulisemaid Ă€ri nĂ€itajaid on protsentiilid. NĂ€itaja nĂ€itab, et 95% juhtudest meie sĂŒsteem töötab nii, nagu soovime. Saame leppida, kui kuskil esinevad probleemid, kuna mĂ”istame ĂŒldist suundumust, kui hĂ€sti vĂ”i halvasti kĂ”ik on.
Tööriistad
ELK Stack. Canary rakendamine on vĂ”imalik, kasutades Elasticsearchi â salvestame sinna vead, kui sĂŒndmused toimuvad. Lihtsa API kĂ”ne abil saab igal ajal saada vigade arvu ja vĂ”rrelda seda varasemate ajavahemikega: GET /applg/_cunt?q=level:errr.
Prometheus. TÔestas end hÀsti Infobipis. See vÔimaldab ellu viia mitmemÔÔtmelisi mÔÔdikuid, kuna kasutatakse silte.
Saame kasutada level, instance, teenus, kombineerides need ĂŒhes sĂŒsteemis. Kasutades offset saame nĂ€iteks vaadata, mis oli vÀÀrtus nĂ€dal tagasi, lihtsalt ĂŒhe kĂ€su abil GET /api/v1/query?query={query}, kus {query}:
rate(logback_appender_total{
level="error",
instance=~"$instance"
}[5m] offset $offset_value)Versioonide analĂŒĂŒs
On mitmeid versioonide analĂŒĂŒsi strateegiaid.
Vaadata mÔÔdikud ainult canary-node'i kohta. Ăks lihtsamaid variante: tĂ”stsime ĂŒles uue versiooni ja uurime ainult selle tööd. Kuid kui insener sel ajal hakkab logisid uurima, pidevalt nĂ€rviliselt lehti vĂ€rskendades, siis ei erine see lahendus ĂŒlejÀÀnutest.
Canary-node'I vÔrreldakse mis tahes teise node'iga. See on vÔrreldes teiste instantsidega, mis töötavad tÀiskoormusel. NÀiteks, kui vÀiksema liikluse korral on olukord halvem vÔi mitte parem kui tegelikel instantsidel, siis on midagi valesti.
Canary-node'i vÔrreldakse iseendaga minevikus. Canary'le eraldatud noodid on vÔrreldavad ajalooliste andmetega. NÀiteks, kui nÀdal tagasi oli kÔik hÀsti, saame neid andmeid kasutada, et mÔista hetkeolukorda.
Automatiseerimine
Soovime vabastada insenerid kÀsitsi vÔrdlemisest, seetÔttu on oluline rakendada automatiseerimist. Deploi (deploy pipeline) protsess nÀeb tavaliselt vÀlja selline:
- alustame;
- eemaldame noodist koormustasakaalust;
- seame canary-noodi;
- lĂŒlitame koormustasakaalu piiritletud liiklusega;
- vÔrdleme.

Selles etapis rakendame automaatset vÔrdlemist. Kuidas see vÀlja nÀeb ja miks on see parem kui kontrollimine pÀrast deploi'i, vaatame Jenkins'i nÀite pÔhjal.
See on pipeline Groovy'le.
while (System.currentTimeMillis() < endCanaryTs) {
def isOk = compare(srv, canary, time, base, offset, metrics)
if (isOk) {
sleep DEFAULT SLEEP
} else {
echo "Canary ebaÔnnestus, tuleb tagasi pöörduda"
return false
}
} Siin tsĂŒklis mÀÀrame, et vĂ”rreldeme uut nooti tunni jooksul. Kui canary protsess ei ole veel lĂ”ppenud â kutsume funktsiooni. See teatab, kas kĂ”ik on hĂ€sti vĂ”i mitte: def isOk = compare(srv, canary, time, base, offset, metrics).
Kui kĂ”ik on hĂ€sti â sleep DEFAULT SLEEP, nĂ€iteks ĂŒhe sekundi ja jĂ€tkame. Kui mitte, siis vĂ€lisma â deploi ei Ă”nnestunud.
MÔÔdiku kirjeldus. Vaatame, kuidas funktsioon compare nÀeb vÀlja DSL'i nÀitel.
metric(
'errorCounts',
'rate(errorCounts{node=~"$canaryInst"}[5m] offset $offset)',
{ baseValue, canaryValue ->
if (canaryValue > baseValue * 1.3) return false
return true
}
)Oletame, et vÔrdleme vigade arvu ja tahame teada vigade arvu sekundis viimase 5 minuti jooksul.
Meil on kaks vÀÀrtust: baasile ja canary-noodile. Canarı-noodi vÀÀrtus â praegune. AlusvÀÀrtus â baseValue â on vÀÀrtus igast muust mitte canary-noodist. VĂ”rdleme vÀÀrtusi omavahel oma kogemuste ja vaatlustest tuleneva valemi jĂ€rgi. Kui canaryValue vÀÀrtus on halb, deploi ebaĂ”nnestus ja me peame tagasi pöörduma.
Miks seda kÔike on vaja?
Inimene ei saa kontrollida sadu ja tuhandeid mÔÔdikuid, veel vÀhem teha seda kiiresti. Automaatne vÔrdlemine aitab kontrollida kÔiki mÔÔdikuid ja kiiruselt probleemidest teavitada. Teavitamise aeg on kriitiline: kui midagi juhtus viimase 2 sekundi jooksul, on kahju vÀiksem, kui see oleks juhtunud 15 minutit tagasi. Seni, kuni keegi probleemist mÀrku annab, kirjutab tuge, ja tugiteenus soovib, et pöördume tagasi, vÔime kliente kaotada.
Kui protsess on lĂ€bitud ja kĂ”ik on hĂ€sti, deploy-ime kĂ”ik ĂŒlejÀÀnud node'id automaatselt. Sel ajal ei tee insenerid midagi. Ainult siis, kui nad kĂ€ivitavad canary, otsustavad nad, millised mÔÔdikud vĂ”tta, kui kaua vĂ”rreldakse ja millist strateegiat kasutada.

Kui tekivad probleemid â tagastame automaatselt canary-node'i, töötame varasemate versioonide peal ja parandame leitud vead. MÔÔdikute pĂ”hjal on neid lihtne leida ja uusi versioone mĂ”jutavaid probleeme nĂ€ha.
Takistused
Seda ellu viia on muidugi keeruline. Esiteks on vajalik ĂŒldine jĂ€lgimissĂŒsteem. Inseneridel on oma mÔÔdikud, tugiteenusel ja analĂŒĂŒtikutel on teised, ning Ă€ril kolmandad. Ăhine sĂŒsteem on ĂŒhine keel, millega Ă€ri ja arendus rÀÀgivad.
Peame praktikas kontrollima mÔÔdikute stabiilsust. Kontrollimine aitab mÔista, milline minimaalne mÔÔdikute komplekt on vajalik kvaliteedi tagamiseks..
Kuidas seda saavutada? Kasutada canary-teenust mitte deploy hetkel. Lisame vanale versioonile mingi teenuse, mis igal hetkel suudab vÔtta mistahes eraldatud node'i, vÀhendada liiklust ilma deploy'ta. SeejÀrel vÔrreldame: uurime vigu ja otsime seda piiri, mil saavutame kvaliteedi.

Millist kasu oleme saanud canary-vÀljalaskmistest
Minimeerisime veadest tulenevate kahjude protsenti. Enamik deploy vigu toimub andmete ebakĂ”lade vĂ”i prioriteetide tĂ”ttu. Selliseid vigu on nĂŒĂŒd palju vĂ€hem, sest suudame probleemi lahendada esimestel sekunditel.
Optimeerisime meeskondade tööd. Uutel liikmetel on "viga teha": nad saavad deploy'ida tootmisse hirmu tundmata, tekkib lisainitsiatiiv ja stiimul töötada. Kui nad midagi rikuvad, siis see ei ole kriitiline ja vale teinud isikut ei vallandata.
Automatiseerisime deploy. See ei ole enam nagu varem kÀsitsi protsess, vaid tÔeline automatiseeritud. Kuid see vÔtab kauem aega.
Toime on prioriseeritud oluliste mÔÔdikute. Kogu ettevĂ”te, alates Ă€ri ja inseneridest, mĂ”istab, mis on meie tootes tĂ”eliselt oluline, nĂ€iteks kasutajate vĂ€ljavool ja sissetulek. Me kontrollime protsessi: testime mÔÔdikuid, sisestame uusi, jĂ€lgime, kuidas vanad töötavad, et luua sĂŒsteem, mis teenib raha efektiivsemalt.
Meil on palju lahedaid praktikaid ja sĂŒsteeme, mis aitavad meid. Sellegipoolest pĂŒĂŒame olla professionaalid ja teha oma tööd kvaliteetselt, olenemata sellest, kas meil on sĂŒsteem, mis aitab, vĂ”i mitte.
Inseneritehnilised lĂ€henemised ja praktikad â . Kui olete saavutanud edusamme tehnilise tĂ€iuslikkuse poole ja olete valmis rÀÀkima, mis teid selles aitas, â .
Plaanime korraldada 8. juunil. MĂ”istame, et praegu on keeruline teha otsuseid konverentsil osalemise kohta. Kuid samas usume, et karantiin ei ole pĂ”hjus kutseaegset suhtlemist ja arengut peatada. SeetĂ”ttu leiame igal juhul viisi arutada tech lead'i ĂŒlesandeid ja nende lahendamise lĂ€henemisviise â kui on vajalik, liikuge veebisisse ja kĂ€ivitame seal vĂ”rgustiku!
Allikas: habr.com
