{"id":31304,"date":"2019-10-31T21:40:34","date_gmt":"2019-10-31T18:40:34","guid":{"rendered":"https:\/\/prohoster.info\/blog\/evolyutsiya-ci-v-komande-mobilnoj-razrabotki\/"},"modified":"2019-10-31T21:40:34","modified_gmt":"2019-10-31T18:40:34","slug":"evolyutsiya-ci-v-komande-mobilnoj-razrabotki","status":"publish","type":"post","link":"https:\/\/prohoster.info\/sv\/blog\/administrirovanie\/evolyutsiya-ci-v-komande-mobilnoj-razrabotki","title":{"rendered":"CI:s evolution inom mobilutvecklingsteam","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Idag utvecklas de flesta programvaruprodukter i team. Villkoren f\u00f6r framg\u00e5ngsrik teamutveckling kan sammanfattas i en enkel schema.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/b283cfd7772d8f479be0754ebbe51b19.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nN\u00e4r du har skrivit koden m\u00e5ste du se till att den:<\/p>\n<ol>\n<li>Fungerar.<\/li>\n<li>Inte bryter n\u00e5got, inklusive koden som dina kollegor har skrivit.<\/li>\n<\/ol>\n<p>\nOm b\u00e5da villkoren \u00e4r uppfyllda \u00e4r du p\u00e5 v\u00e4g mot framg\u00e5ng. F\u00f6r att enkelt kunna kontrollera dessa villkor och inte avvika fr\u00e5n den l\u00f6nsamma v\u00e4gen, har Continuous Integration utvecklats.<\/p>\n<p>CI \u00e4r en arbetsprocess d\u00e4r du s\u00e5 ofta som m\u00f6jligt integrerar din kod med den gemensamma koden f\u00f6r produkten. Och inte bara integrerar, utan ocks\u00e5 st\u00e4ndigt kontrollerar att allt fungerar. Eftersom det \u00e4r mycket som beh\u00f6ver kontrolleras och ofta, \u00e4r det v\u00e4rt att t\u00e4nka p\u00e5 automatisering. Man kan g\u00f6ra allt manuellt, men det \u00e4r inte att rekommendera, och h\u00e4r \u00e4r varf\u00f6r.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<ul>\n<li><strong>M\u00e4nniskor \u00e4r dyra<\/strong>. En timme arbete f\u00f6r en programmerare kostar mer \u00e4n en timme arbete f\u00f6r en server.<\/li>\n<li><strong>M\u00e4nniskor g\u00f6r misstag<\/strong>. D\u00e4rf\u00f6r kan det uppst\u00e5 situationer n\u00e4r tester k\u00f6rs p\u00e5 fel gren eller n\u00e4r fel begcommit har byggts f\u00f6r testarna.<\/li>\n<li><strong>M\u00e4nniskor \u00e4r lata<\/strong>. Periodvis, n\u00e4r jag avslutar en uppgift, har jag tanken: \"Vad ska jag ens kontrollera? Jag har skrivit tv\u00e5 rader \u2013 det fungerar garanterat!\" Jag tror att n\u00e5gra av er ocks\u00e5 ibland f\u00e5r s\u00e5dana tankar. Men man m\u00e5ste alltid kontrollera.<\/li>\n<\/ul>\n<p>\nHur Continuous Integration implementerades och utvecklades i Avitos mobila utvecklingsteam, hur man gick fr\u00e5n 0 till 450 byggningar per dag, och vad byggmaskinerna samlar in 200 timmar per dag, ber\u00e4ttar Nikolai Nesterov (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/nnesterov\/\" class=\"user_link\">nnesterov<\/a><\/noindex>) \u2013 deltagare i alla evolution\u00e4ra f\u00f6r\u00e4ndringar av CI\/CD Android-applikationen.<\/p>\n<p>Ber\u00e4ttelsen baseras p\u00e5 exemplet av Android-teamet, men de flesta metoder \u00e4r ocks\u00e5 till\u00e4mpliga p\u00e5 iOS.<\/p>\n<p><center><div class=\"youtube-placeholder\" data-id=\"lz8MNATTUCU\" onclick=\"loadVideo(this)\">\r\n        <img decoding=\"async\" src=\"https:\/\/img.youtube.com\/vi\/lz8MNATTUCU\/hqdefault.jpg\" alt=\"Spela video\" loading=\"lazy\" width=\"480\" height=\"360\" style=\"width:100%;height:auto;\">\r\n        <div class=\"play-button\"><\/div>\r\n    <\/div><\/center><br \/>\nEn g\u00e5ng i tiden arbetade bara en person i Avitos Android-team. F\u00f6r honom beh\u00f6vdes ingenting fr\u00e5n Continuous Integration, eftersom det inte fanns n\u00e5gon att integrera med.<\/p>\n<p>Men applikationen v\u00e4xte, fler och fler nya uppgifter d\u00f6k upp, och d\u00e4rmed v\u00e4xte teamet. Vid en viss tidpunkt kom tiden att mer formellt organisera kodintegrationsprocessen. Det beslutades att anv\u00e4nda Git flow.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/f433effc5ab5a59de3d6bd20a7a87057.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nGit flow-konceptet \u00e4r v\u00e4lk\u00e4nt: i projektet finns en gemensam branch, develop, och f\u00f6r varje ny funktion skapar utvecklarna en separat branch, g\u00f6r sina commits, pushar och n\u00e4r de vill f\u00f6ra in sin kod i develop-branchen \u00f6ppnar de en pull request. F\u00f6r kunskapsutbyte och diskussion av metoder har vi inf\u00f6rt code review, vilket inneb\u00e4r att kollegor m\u00e5ste granska och bekr\u00e4fta varandras kod.<\/p>\n<h2>Kontroller<\/h2>\n<p>\nAtt se p\u00e5 koden \u00e4r bra, men otillr\u00e4ckligt. D\u00e4rf\u00f6r inf\u00f6rs automatiska kontroller.<\/p>\n<ul>\n<li>F\u00f6rst kontrollerar vi <strong>ARC-byggnaden<\/strong>.<\/li>\n<li>M\u00e5nga <strong>Junit-tester<\/strong>.<\/li>\n<li><strong>Vi ber\u00e4knar code coverage<\/strong>, eftersom vi k\u00f6r tester.<\/li>\n<\/ul>\n<p>\nF\u00f6r att f\u00f6rst\u00e5 hur dessa kontroller ska k\u00f6ras, l\u00e5t oss titta p\u00e5 utvecklingsprocessen p\u00e5 Avito.<\/p>\n<p>Det kan schematiskt representeras s\u00e5 h\u00e4r:<\/p>\n<ul>\n<li>Utvecklaren skriver kod p\u00e5 sin dator. Det \u00e4r m\u00f6jligt att k\u00f6ra integrationskontroller h\u00e4r \u2014 antingen via commit-hook eller bara k\u00f6ra kontrollerna i bakgrunden.<\/li>\n<li>N\u00e4r utvecklaren har pushat koden \u00f6ppnar han en pull request. F\u00f6r att hans kod ska hamna i develop-branchen m\u00e5ste han klara code review och f\u00e5 tillr\u00e4ckligt med bekr\u00e4ftelser. Kontroller och byggen kan aktiveras h\u00e4r: medan inte alla byggen \u00e4r framg\u00e5ngsrika kan inte pull request sl\u00e5s ihop.<\/li>\n<li>N\u00e4r pull requesten har slagits ihop och koden har hamnat i develop kan man v\u00e4lja ett l\u00e4mpligt tillf\u00e4lle: till exempel p\u00e5 natten, n\u00e4r alla servrar \u00e4r lediga, och k\u00f6ra kontroller hur mycket som helst.<\/li>\n<\/ul>\n<p>\nAtt k\u00f6ra kontroller p\u00e5 sin dator gillade ingen. N\u00e4r utvecklaren har slutf\u00f6rt sin funktion vill han snabbt push-a den och \u00f6ppna en pull request. Om det vid den tidpunkten k\u00f6rs n\u00e5gra l\u00e5nga kontroller \u00e4r det inte bara obehagligt utan saktar ocks\u00e5 ner utvecklingen: medan datorn kontrollerar n\u00e5got kan han inte arbeta normalt.<\/p>\n<p>Vi gillade mycket att k\u00f6ra kontroller p\u00e5 natten, eftersom det finns gott om tid och servrar, man kan verkligen ta sig tid. Men tyv\u00e4rr, n\u00e4r funktionens kod har kommit in i develop har utvecklaren mycket mindre motivation att fixa de fel som CI har hittat. Jag f\u00e5ngade mig sj\u00e4lv d\u00e5 och d\u00e5 n\u00e4r jag tittade p\u00e5 alla hittade fel i morgonrapporten och t\u00e4nkte att jag skulle fixa dem senare, eftersom det just nu ligger en fantastisk ny uppgift i Jira som jag verkligen vill b\u00f6rja arbeta med.<\/p>\n<p>Om kontroller blockerar pull requesten, finns det motivation nog, eftersom medan byggena inte \u00e4r gr\u00f6na, kommer koden inte in i develop, vilket inneb\u00e4r att uppgiften inte kommer att bli slutf\u00f6rd.<\/p>\n<p>Till slut valde vi en strategi d\u00e4r vi k\u00f6r en maximal upps\u00e4ttning tester p\u00e5 natten, och de mest kritiska och framf\u00f6r allt snabba testerna k\u00f6r vi vid pull request. Men vi stannar inte d\u00e4r \u2014 samtidigt optimerar vi hastigheten p\u00e5 testerna s\u00e5 att vi kan flytta dem fr\u00e5n nattl\u00e4ge till pull request-tester.<\/p>\n<p>Vid den tiden gick alla v\u00e5ra byggen tillr\u00e4ckligt snabbt, s\u00e5 vi aktiverade bara blockeraren f\u00f6r pull request-bygget av ARC, Junit-tester och ber\u00e4kning av kodt\u00e4ckning. Vi aktiverade det, funderade \u2014 och beslutade oss f\u00f6r att avst\u00e5 fr\u00e5n kodt\u00e4ckning, eftersom vi ans\u00e5g att det inte var n\u00f6dv\u00e4ndigt.<\/p>\n<p><strong><em>Det tog oss tv\u00e5 dagar att konfigurera grundl\u00e4ggande CI (denna tidsuppskattning \u00e4r ungef\u00e4rlig och beh\u00f6vs f\u00f6r perspektivet). <\/em><\/strong><\/p>\n<p>Efter det b\u00f6rjade vi t\u00e4nka vidare \u2014 testerar vi \u00f6verhuvudtaget korrekt? K\u00f6r vi byggen vid pull request p\u00e5 r\u00e4tt s\u00e4tt?<\/p>\n<p>Vi k\u00f6rde bygget p\u00e5 den senaste commit av grenen fr\u00e5n vilken pull requesten \u00f6ppnades. Men testerna av denna commit kan bara visa att den kod som utvecklaren skrivit fungerar. De bevisar inte att han eller hon inte br\u00f6t n\u00e5got. Egentligen m\u00e5ste vi kontrollera tillst\u00e5ndet p\u00e5 utvecklingsgrenen efter att funktionen har blivit integrerad.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/c1a179b68b02c04031177f9bf51a81ed.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nF\u00f6r detta skrev vi ett enkelt bash-skript. <strong>premerge.sh:<\/strong><\/p>\n<pre><code class=\"bash\">#!\/usr\/bin\/env bash\n\nset -e\n\ngit fetch origin develop\n\ngit merge origin\/develop<\/code><\/pre>\n<p>\nH\u00e4r dras helt enkelt de senaste \u00e4ndringarna fr\u00e5n utvecklingsgrenen och sl\u00e5s samman med den aktuella grenen. Vi lade till skriptet premerge.sh som det f\u00f6rsta steget av alla byggen och b\u00f6rjade kontrollera just det vi vill, det vill s\u00e4ga <strong>integrationen<\/strong>.<\/p>\n<p><strong><em>Det tog tre dagar att lokalisera problemen, hitta l\u00f6sningen och skriva detta skript.<\/em><\/strong><\/p>\n<p>Applikationen utvecklades, uppgifterna blev fler, teamet v\u00e4xte, och ibland b\u00f6rjade premerge.sh att svika oss. Konfliktande \u00e4ndringar tr\u00e4ngde in i utvecklingsgrenen och br\u00f6t bygget.<\/p>\n<p>Ett exempel p\u00e5 hur detta sker:<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/6762d9ce4e431549455c49f2317a8f20.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nTv\u00e5 utvecklare b\u00f6rjar samtidigt arbeta med funktionerna A och B. Utvecklaren av funktion A uppt\u00e4cker en oanv\u00e4nd funktion i projektet, <code>answer()<\/code> och, som en bra scout, tar bort den. Samtidigt l\u00e4gger utvecklaren av funktion B till ett nytt anrop av denna funktion i sin gren.<\/p>\n<p>Utvecklarna avslutar sitt arbete och \u00f6ppnar samtidigt en pull request. Byggen startar, premerge.sh kontrollerar b\u00e5da pull requesterna mot det aktuella tillst\u00e5ndet p\u00e5 utvecklingsgrenen \u2014 alla tester \u00e4r gr\u00f6na. Efter det merges pull request A, pull request B... Boom! Utvecklingsgrenen kraschade, eftersom det finns ett anrop av en icke-existerande funktion i koden.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/0261dcc9f7f2e5e018ab136081b4679b.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nN\u00e4r utvecklingsgrenen inte g\u00e5r att bygga, <strong>lokal katastrof<\/strong>. Hela teamet kan inte samla n\u00e5got och ge det f\u00f6r testning.<\/p>\n<p>S\u00e5 blev det att jag oftast \u00e4gnade mig \u00e5t infrastrukturuppgifter: analys, n\u00e4tverk, databaser. Det var jag som skrev de funktioner och klasser som andra utvecklare anv\u00e4nder. D\u00e4rf\u00f6r hamnade jag ofta i liknande situationer. Jag hade till och med en tid bild p\u00e5 det h\u00e4r.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/0eb1aadd05b33ce995027ab52d5c1e53.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nEftersom detta inte passade oss b\u00f6rjade vi arbeta p\u00e5 alternativ f\u00f6r hur vi kunde f\u00f6rhindra det.<\/p>\n<h2>Hur man inte bryter develop<\/h2>\n<p>\nF\u00f6rsta alternativet: <strong>\u00e5terkompilera alla pull requests vid uppdatering av develop. <\/strong>Om i v\u00e5rt exempel pull request med funktion A f\u00f6rst g\u00e5r in i develop, kommer pull request f\u00f6r funktion B att \u00e5terkompileras, och d\u00e4rmed kommer kontrollerna att misslyckas p\u00e5 grund av kompilationsfel.<\/p>\n<p>F\u00f6r att f\u00f6rst\u00e5 hur mycket tid detta kommer att ta, l\u00e5t oss titta p\u00e5 ett exempel med tv\u00e5 PR. Vi \u00f6ppnar tv\u00e5 PR: tv\u00e5 byggen, tv\u00e5 k\u00f6rningar av kontroller. Efter att den f\u00f6rsta PR har slagits samman med develop m\u00e5ste den andra \u00e5terkompileras. Totalt tar det tre k\u00f6rningar av kontroller f\u00f6r tv\u00e5 PR: 2 + 1 = 3.<\/p>\n<p>I princip okej. Men vi tittade p\u00e5 statistiken, och en typisk situation i v\u00e5rt team var 10 \u00f6ppna PR, d\u00e5 blir antalet kontroller summan av progressionen: 10 + 9 + \u2026 + 1 = 55. Det vill s\u00e4ga f\u00f6r att godk\u00e4nna 10 PR m\u00e5ste man \u00e5terkompilera 55 g\u00e5nger. Och det \u00e4r i en ideal situation, d\u00e4r alla kontroller g\u00e5r igenom p\u00e5 f\u00f6rsta f\u00f6rs\u00f6ket, d\u00e4r ingen \u00f6ppnar n\u00e5gon ytterligare pull request medan denna tio stycken bearbetas.<\/p>\n<p>F\u00f6rest\u00e4ll dig att du \u00e4r en utvecklare som m\u00e5ste hinna trycka p\u00e5 knappen \"merge\" f\u00f6rst, f\u00f6r om det g\u00f6rs av grannen s\u00e5 f\u00e5r man v\u00e4nta p\u00e5 att alla byggen ska genomf\u00f6ras igen... Nej, s\u00e5 h\u00e4r kan vi inte ha det, det kommer allvarligt att f\u00f6rdr\u00f6ja utvecklingen.<\/p>\n<p>Andra m\u00f6jliga s\u00e4ttet: <strong>kompilera pull request efter code review. <\/strong>Det vill s\u00e4ga du \u00f6ppnar en pull request, samlar in det n\u00f6dv\u00e4ndiga antalet godk\u00e4nnanden fr\u00e5n kollegor, \u00e5tg\u00e4rdar vad som beh\u00f6vs, och f\u00f6rst d\u00e5 k\u00f6r du byggen. Om de \u00e4r framg\u00e5ngsrika, sl\u00e5s pull request ihop med develop. I detta fall finns det inga ytterligare omstarter, men feedbacken blir betydligt l\u00e5ngsammare. Jag, som utvecklare, vill direkt se om min pull request kan byggas. Till exempel, om ett test misslyckas, beh\u00f6ver jag snabbt fixa det. I h\u00e4ndelse av f\u00f6rsenad kompilering s\u00e4nks feedbacken, vilket ocks\u00e5 s\u00e4nker hela utvecklingen. Det passade oss inte heller.<\/p>\n<p>Till slut kvarstod bara det tredje alternativet - <strong>att cykla<\/strong>. All v\u00e5r kod, alla v\u00e5ra k\u00e4llkoder lagras i ett repository p\u00e5 Bitbucket-servern. D\u00e4rf\u00f6r var vi tvungna att utveckla en plugin f\u00f6r Bitbucket.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/5e4b1f770ea1a3449b616c3101640294.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u042d\u0442\u043e\u0442 \u043f\u043b\u0430\u0433\u0438\u043d \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0441\u043b\u0438\u044f\u043d\u0438\u044f pull request&#8217;\u043e\u0432. \u041d\u0430\u0447\u0430\u043b\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0435: \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f PR, \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f \u0432\u0441\u0435 \u0441\u0431\u043e\u0440\u043a\u0438, \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 code review. \u041d\u043e \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a code review \u043f\u0440\u043e\u0439\u0434\u0435\u043d, \u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0440\u0435\u0448\u0430\u0435\u0442 \u043d\u0430\u0436\u0430\u0442\u044c \u043d\u0430 \u00abmerge\u00bb, \u043f\u043b\u0430\u0433\u0438\u043d \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043a\u0430\u043a\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f develop \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0438\u0441\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438. \u0415\u0441\u043b\u0438 \u043f\u043e\u0441\u043b\u0435 \u0431\u0438\u043b\u0434\u043e\u0432 develop \u0443\u0441\u043f\u0435\u043b \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f, \u043f\u043b\u0430\u0433\u0438\u043d \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0432\u043b\u0438\u0442\u044c \u0442\u0430\u043a\u043e\u0439 pull request \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0432\u0435\u0442\u043a\u0443. \u041e\u043d \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442 \u0431\u0438\u043b\u0434\u044b \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0432\u0435\u0436\u0435\u0433\u043e develop.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/0edee880e1f2d286a0c64033103ac136.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nI v\u00e5rt exempel med konfliktfyllda \u00e4ndringar kommer s\u00e5dana byggen inte att passera p\u00e5 grund av kompilatorfel. D\u00e4rf\u00f6r m\u00e5ste utvecklaren av funktion B r\u00e4tta koden och starta om testerna, s\u00e5 till\u00e4mpas pull requesten automatiskt av pluginen.<\/p>\n<p>Innan vi implementerade denna plugin hade vi i genomsnitt 2,7 tester som k\u00f6rdes per pull request. Med pluginen blev det 3,6 tester. Det var vi n\u00f6jda med.<\/p>\n<p>Det \u00e4r v\u00e4rt att notera att denna plugin har en nackdel: den startar om bygget endast en g\u00e5ng. Det finns allts\u00e5 fortfarande en liten m\u00f6jlighet f\u00f6r konfliktfyllda \u00e4ndringar att komma in i develop. Men sannolikheten f\u00f6r detta \u00e4r l\u00e5g, och vi accepterade denna kompromiss mellan antalet k\u00f6rningar och risken f\u00f6r problem. Under tv\u00e5 \u00e5r intr\u00e4ffade det bara en g\u00e5ng, s\u00e5 kanske var det inte f\u00f6rg\u00e4ves.<\/p>\n<p><strong><em>Det tog oss tv\u00e5 veckor att skriva den f\u00f6rsta versionen av pluggen f\u00f6r Bitbucket. <\/em><\/strong><\/p>\n<h3>Nya tester<\/h3>\n<p>\nUnder tiden fortsatte v\u00e5rt team att v\u00e4xa. Nya tester lades till.<\/p>\n<p>Vi t\u00e4nkte: varf\u00f6r \u00e5tg\u00e4rda fel n\u00e4r vi kan f\u00f6rhindra dem? Och d\u00e4rf\u00f6r implementerade vi <strong>statisk kodanalys<\/strong>. Vi b\u00f6rjade med lint, som ing\u00e5r i Android SDK. Men d\u00e5 kunde den helt och h\u00e5llet inte arbeta med Kotlin-kod, och vi hade redan 75% av applikationen skriven i Kotlin. D\u00e4rf\u00f6r lades inbyggda till <strong>Android Studio checks.<\/strong><\/p>\n<p>F\u00f6r detta beh\u00f6vde vi verkligen vrida och v\u00e4nda: ta Android Studio, paketera den i Docker och k\u00f6ra den p\u00e5 CI med en virtuell sk\u00e4rm, s\u00e5 att den trodde att den k\u00f6rdes p\u00e5 en riktig b\u00e4rbar dator. Men det fungerade.<\/p>\n<p>Samtidigt b\u00f6rjade vi ocks\u00e5 skriva m\u00e5nga <strong>instrumentation-tester<\/strong> och implementerade <strong>screenshot-testning.<\/strong>Det h\u00e4r \u00e4r n\u00e4r en referenssk\u00e4rmdump genereras f\u00f6r en separat liten vy, och testet g\u00e5r ut p\u00e5 att ta en sk\u00e4rmdump fr\u00e5n vyn och j\u00e4mf\u00f6ra den pixel f\u00f6r pixel med referensen. Om det finns avvikelser betyder det att layouten har blivit fel eller att n\u00e5got inte st\u00e4mmer med stilarna.<\/p>\n<p>Men instrumentationstester och sk\u00e4rmdumpstester m\u00e5ste k\u00f6ras p\u00e5 enheter: p\u00e5 emulatorer eller p\u00e5 verkliga enheter. Med tanke p\u00e5 att det finns m\u00e5nga tester som k\u00f6rs ofta beh\u00f6vs en hel farm. Att uppr\u00e4tta sin egen farm \u00e4r f\u00f6r arbetskr\u00e4vande, s\u00e5 vi hittade ett f\u00e4rdigt alternativ \u2014 Firebase Test Lab.<\/p>\n<h3>Firebase Test Lab<\/h3>\n<p>\nValdes eftersom Firebase \u00e4r en produkt fr\u00e5n Google, vilket inneb\u00e4r att den b\u00f6r vara p\u00e5litlig och knappast kommer att d\u00f6 ut. Priserna \u00e4r \u00f6verkomliga: 5 $ per timme f\u00f6r en verklig enhet, 1 $ per timme f\u00f6r en emulator.<\/p>\n<p><strong><em>Implementeringen av Firebase Test Lab i v\u00e5r CI tog ungef\u00e4r tre veckor.<\/em><\/strong><\/p>\n<p>Men teamet fortsatte att v\u00e4xa, och tyv\u00e4rr b\u00f6rjade Firebase svika oss. Vid den tidpunkten hade den inga SLA:er. Ibland tvingade Firebase oss att v\u00e4nta p\u00e5 att r\u00e4tt antal enheter skulle bli tillg\u00e4ngliga f\u00f6r tester, ist\u00e4llet f\u00f6r att k\u00f6ra dem direkt som vi ville. V\u00e4ntetiden i k\u00f6n kunde ta upp till en halvtimme, vilket \u00e4r mycket l\u00e4nge. Instrumentationstester k\u00f6rdes p\u00e5 varje PR, och f\u00f6rdr\u00f6jningarna bromsade ner utvecklingen, och sedan fick vi en m\u00e5nadsr\u00e4kning som var ganska h\u00f6g. Sammanfattningsvis beslutades det att avst\u00e5 fr\u00e5n Firebase och utveckla in-house, eftersom teamet hade blivit tillr\u00e4ckligt stort.<\/p>\n<h3>Docker + Python + bash<\/h3>\n<p>\nVi tog docker, stoppade in emulatorerna i den, och skrev ett enkelt program i Python som n\u00e4r det beh\u00f6vdes startar ett visst antal emulatorer i r\u00e4tt version och n\u00e4r vi beh\u00f6ver stoppar dem. Och s\u00e5klart, n\u00e5gra bash-skript \u2014 hur skulle vi klara oss utan dem?<\/p>\n<p><strong><em>Det tog fem veckor att skapa v\u00e5r egen testmilj\u00f6.<\/em><\/strong><\/p>\n<p>Som resultat hade varje pull request en omfattande, blockerande lista med kontroller:<\/p>\n<ul>\n<li>Bygg av APK;<\/li>\n<li>Junit-tester;<\/li>\n<li>Lint;<\/li>\n<li>Android Studio-kontroller;<\/li>\n<li>Instrumentationstester;<\/li>\n<li>Sk\u00e4rmdumpstester.<\/li>\n<\/ul>\n<p>\nDetta f\u00f6rhindrade m\u00e5nga m\u00f6jliga fel. Tekniskt sett fungerade allt, men utvecklarna klagade p\u00e5 att det tog f\u00f6r l\u00e5ng tid att f\u00e5 resultat.<\/p>\n<p>F\u00f6r l\u00e5ng tid \u2014 hur l\u00e5ng tid \u00e4r det? Vi extraherade data fr\u00e5n Bitbucket och TeamCity till ett analysystem och ins\u00e5g att <strong>den genomsnittliga v\u00e4ntetiden var 45 minuter<\/strong>. Det betyder att en utvecklare i genomsnitt v\u00e4ntar 45 minuter p\u00e5 byggresultaten n\u00e4r de \u00f6ppnar en pull request. Enligt min mening \u00e4r det alldeles f\u00f6r mycket och s\u00e5 kan vi inte arbeta.<\/p>\n<p>Sj\u00e4lvklart best\u00e4mde vi oss f\u00f6r att snabba upp alla v\u00e5ra byggprocesser.<\/p>\n<h2>Vi accelererar<\/h2>\n<p>\nN\u00e4r vi s\u00e5g att byggen ofta stod i k\u00f6, b\u00f6rjade vi f\u00f6rst och fr\u00e4mst <strong>k\u00f6pa mer h\u00e5rdvara<\/strong> \u2014 extensiv utveckling \u00e4r det enklaste. Byggen slutade st\u00e5 i k\u00f6, men v\u00e4ntetiden minskade bara lite, eftersom vissa kontroller sj\u00e4lva tog v\u00e4ldigt l\u00e5ng tid.<\/p>\n<h3>Vi tar bort alltf\u00f6r l\u00e5nga kontroller<\/h3>\n<p>\nV\u00e5r Continuous Integration kunde f\u00e5nga s\u00e5dana typer av fel och problem.<\/p>\n<ul>\n<li><strong>Kan inte byggas<\/strong>. CI kan f\u00e5nga ett kompilationsfel n\u00e4r inget kan byggas p\u00e5 grund av konflikterande f\u00f6r\u00e4ndringar. Som jag redan n\u00e4mnt kan ingen bygga n\u00e5got, utvecklingen stannar av, och alla \u00e4r nerv\u00f6sa.<\/li>\n<li><strong>Fel i beteende<\/strong>. Till exempel n\u00e4r applikationen byggs, men den kraschar n\u00e4r man trycker p\u00e5 en knapp, eller knappen g\u00e5r \u00f6verhuvudtaget inte att trycka p\u00e5. Det \u00e4r d\u00e5ligt, eftersom s\u00e5dana fel kan n\u00e5 anv\u00e4ndaren.<\/li>\n<li><strong>Fel i layout<\/strong>. Till exempel n\u00e4r knappen g\u00e5r att trycka p\u00e5, men den har f\u00f6rflyttats 10 pixlar \u00e5t v\u00e4nster.<\/li>\n<li><strong>\u00d6kning av teknisk skuld<\/strong>.<\/li>\n<\/ul>\n<p>\nGenom att titta p\u00e5 denna lista ins\u00e5g vi att endast de f\u00f6rsta tv\u00e5 punkterna \u00e4r kritiska. Dessa problem vill vi f\u00e5nga f\u00f6rst och fr\u00e4mst. Layoutfel uppt\u00e4cktes under design-granskningen och kan l\u00e4tt r\u00e4ttas till d\u00e5. Arbetet med teknisk skuld kr\u00e4ver en separat process och planering, d\u00e4rf\u00f6r best\u00e4mde vi oss f\u00f6r att inte kontrollera det p\u00e5 pull request.<\/p>\n<p>Utifr\u00e5n denna klassificering gick vi igenom hela listan med kontroller. <strong>Vi str\u00f6k Lint<\/strong> och flyttade dess k\u00f6rning till natten: bara f\u00f6r att den skulle ge rapporten om hur m\u00e5nga problem som finns i projektet. Med teknisk skuld kom vi \u00f6verens om att arbeta separat, och <strong>vi avstod fr\u00e5n Android Studio-kontroller helt<\/strong>. Android Studio i Docker f\u00f6r att k\u00f6ra inspektioner l\u00e5ter intressant, men ger m\u00e5nga problem med underh\u00e5llet. Varje uppdatering av versioner av Android Studio inneb\u00e4r en kamp med of\u00f6rklarliga fel. Det var ocks\u00e5 sv\u00e5rt att st\u00f6dja sk\u00e4rmbildstester, eftersom biblioteket fungerade inte s\u00e4rskilt stabilt, det f\u00f6rekom falska positiva. <strong>Vi tog bort sk\u00e4rmbildstester fr\u00e5n kontrollistan<\/strong>.<\/p>\n<p>Till slut har vi kvar:<\/p>\n<ul>\n<li>Bygg av APK;<\/li>\n<li>Junit-tester;<\/li>\n<li>Instrumentation tests.<\/li>\n<\/ul>\n<h3>Gradle remote cache<\/h3>\n<p>\nUtan tunga kontroller har allt blivit b\u00e4ttre. Men det finns ingen gr\u00e4ns f\u00f6r perfektion!<\/p>\n<p>V\u00e5r applikation var redan uppdelad i cirka 150 gradle-moduler. Vanligtvis fungerar Gradle remote cache bra i s\u00e5dana fall, och vi best\u00e4mde oss f\u00f6r att prova det.<\/p>\n<p>Gradle remote cache \u00e4r en tj\u00e4nst som kan cachelager byggartefakter f\u00f6r enskilda uppgifter i specifika moduler. Gradle kontrollerar ist\u00e4llet f\u00f6r att faktiskt kompilera koden, via HTTP, i remote cache om n\u00e5gon redan har k\u00f6rt den h\u00e4r uppgiften. Om s\u00e5 \u00e4r fallet, laddar den helt enkelt ner resultatet.<\/p>\n<p><strong><em>Att starta Gradle remote cache \u00e4r enkelt, eftersom Gradle tillhandah\u00e5ller en Docker-bild. Vi lyckades g\u00f6ra detta p\u00e5 tre timmar.<\/em><\/strong><\/p>\n<p>Det beh\u00f6vdes bara att starta Docker och skriva en rad i projektet. Men \u00e4ven om det g\u00e5r snabbt att starta, kr\u00e4vs det ganska mycket tid f\u00f6r att allt ska fungera bra.<\/p>\n<p>Nedan finns en graf \u00f6ver cache misses.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/458ef23b5506b04f3bd385be0c18607a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nI b\u00f6rjan var andelen cache misses cirka 65%. Efter tre veckor lyckades vi s\u00e4nka detta v\u00e4rde till 20%. Det visade sig att uppgifterna som Android-applikationen bygger hade konstiga transitiva beroenden, vilket gjorde att Gradle missade cachen.<\/p>\n<p>Genom att ansluta cachen lyckades vi avsev\u00e4rt p\u00e5skynda bygget. Men f\u00f6rutom bygget k\u00f6rs ocks\u00e5 instrumentation tester, och de tar l\u00e5ng tid att k\u00f6ra. Kanske beh\u00f6ver vi inte k\u00f6ra alla tester p\u00e5 varje pull request. F\u00f6r att ta reda p\u00e5 detta anv\u00e4nder vi p\u00e5verkananalys.<\/p>\n<h3>P\u00e5verkananalys<\/h3>\n<p>\nP\u00e5 pull request samlar vi git diff och hittar de \u00e4ndrade Gradle-modulerna.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/471ca37206a0da4d5741c8ee1dbb7f03.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDet \u00e4r meningsfullt att bara k\u00f6ra de instrumentation tester som kontrollerar de \u00e4ndrade modulerna och alla moduler som beror p\u00e5 dem. Att k\u00f6ra tester f\u00f6r angr\u00e4nsande moduler \u00e4r inte meningsfullt: d\u00e4r har inget kod \u00e4ndrats, och ingenting kan g\u00e5 s\u00f6nder.<\/p>\n<p>Med instrumentation tester \u00e4r det inte s\u00e5 enkelt, eftersom de m\u00e5ste befinna sig i den \u00f6versta applikationsmodulen. Vi anv\u00e4nde en heuristik baserad p\u00e5 bytekodanalys f\u00f6r att f\u00f6rst\u00e5 vilken modul varje test tillh\u00f6r.<\/p>\n<p><strong><em>Modernisering av arbetet med instrumentation tester, f\u00f6r att de endast ska kontrollera involverade moduler, tog ungef\u00e4r \u00e5tta veckor.<\/em><\/strong><\/p>\n<p>\u00c5tg\u00e4rderna f\u00f6r att p\u00e5skynda kontrollerna fungerade framg\u00e5ngsrikt. Vi gick fr\u00e5n 45 minuter ner till cirka 15. Att v\u00e4nta en kvarts timme p\u00e5 bygget \u00e4r redan acceptabelt.<\/p>\n<p>Men nu har utvecklarna b\u00f6rjat klaga p\u00e5 att de inte f\u00f6rst\u00e5r vilka byggen som k\u00f6rs, var de kan se loggar, varf\u00f6r bygget \u00e4r r\u00f6tt, vilket test som misslyckades etc.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/aa76151174cd4e5a45ff281818e312f5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nProblem med \u00e5terkoppling f\u00f6rdr\u00f6jer utvecklingen, d\u00e4rf\u00f6r har vi str\u00e4vat efter att tillhandah\u00e5lla s\u00e5 tydlig och detaljerad information som m\u00f6jligt om varje PR och build. Vi b\u00f6rjade med kommentarer i Bitbucket f\u00f6r PR d\u00e4r vi angav vilken build som misslyckades och varf\u00f6r, samt skickade riktade meddelanden i Slack. Till slut skapade vi en PR-dashboard som visar en lista \u00f6ver alla builds som f\u00f6r n\u00e4rvarande k\u00f6rs och deras status: i k\u00f6, k\u00f6rs, misslyckad eller avslutad. Man kan klicka p\u00e5 en build och komma till dess logg.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/2d16b7a6b6d23e23903d291ef1526999.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<strong><em>Det har tagit sex veckor att f\u00e5 detaljerad \u00e5terkoppling.<\/em><\/strong><\/p>\n<h2>Planer<\/h2>\n<p>\nVi g\u00e5r vidare till den senaste historien. Efter att ha l\u00f6st fr\u00e5gan om \u00e5terkoppling, har vi n\u00e5tt en ny niv\u00e5 - vi har beslutat att bygga v\u00e5r egen emulatorfarm. N\u00e4r det finns m\u00e5nga tester och emulatorer blir det sv\u00e5rt att hantera dem. Resultatet blev att alla v\u00e5ra emulatorer flyttade till en k8s-kluster med flexibel resursf\u00f6rvaltning.<\/p>\n<p>Dessutom finns det andra planer.<\/p>\n<ul>\n<li><strong>\u00c5terst\u00e4ll Lint<\/strong> (och annan statisk analys). Vi arbetar redan p\u00e5 detta.<\/li>\n<li>K\u00f6r alla <strong>end-to-end tester<\/strong> p\u00e5 alla versioner av SDK.<\/li>\n<\/ul>\n<p>\nS\u00e5, vi har sp\u00e5rat utvecklingen av Continuous Integration p\u00e5 Avito. Nu vill jag ge n\u00e5gra r\u00e5d fr\u00e5n en erfaren.<\/p>\n<h1>Tips<\/h1>\n<p>\nOm jag bara kunde ge ett r\u00e5d, skulle det vara detta:<\/p>\n<blockquote><p>Var sn\u00e4ll och var f\u00f6rsiktig med shell-skript!<\/p><\/blockquote>\n<p>\nBash \u00e4r ett mycket flexibelt och kraftfullt verktyg, det \u00e4r mycket bekv\u00e4mt och snabbt att skriva skript p\u00e5. Men man kan falla i f\u00e4llan, och tyv\u00e4rr har vi gjort det.<\/p>\n<p>Det b\u00f6rjade med enkla skript som k\u00f6rdes p\u00e5 v\u00e5ra byggmaskiner:<\/p>\n<pre><code class=\"bash\">#!\/usr\/bin\/env bash\n.\/gradlew assembleDebug<\/code><\/pre>\n<p>\nMen, som bekant, utvecklas allt med tiden och blir mer komplext - l\u00e5t oss k\u00f6ra ett skript fr\u00e5n ett annat, l\u00e5t oss vidarebefordra n\u00e5gra parametrar - i slut\u00e4ndan var vi tvungna att skriva en funktion som best\u00e4mmer vilken niv\u00e5 av inb\u00e4ddning vi befinner oss p\u00e5, s\u00e5 att vi kan s\u00e4tta in r\u00e4tt citattecken s\u00e5 att allt kan starta.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/244a574f7b5d3b4fc77cfc4ccda2db69.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nKan du f\u00f6rest\u00e4lla dig arbetsinsatsen f\u00f6r att utveckla s\u00e5dana skript? Jag rekommenderar att undvika den h\u00e4r f\u00e4llan.<\/p>\n<p>Vad kan man ers\u00e4tta det med?<\/p>\n<ul>\n<li>Vilket skriptspr\u00e5k som helst. Det \u00e4r bekv\u00e4mare att skriva p\u00e5 <strong>Python eller Kotlin Script<\/strong> eftersom det \u00e4r programmering, inte skript.<\/li>\n<li>Eller beskriva all logik i byggen i form av <strong>Custom gradle tasks<\/strong> f\u00f6r ditt projekt.<\/li>\n<\/ul>\n<p>\nVi valde den andra varianten och nu tar vi systematiskt bort alla bash-skript och skriver m\u00e5nga anpassade gradle-tasks.<\/p>\n<p><strong>R\u00e5d nr 2: Spara infrastrukturen i kod.<\/strong><\/p>\n<p>Det \u00e4r praktiskt n\u00e4r inst\u00e4llningen f\u00f6r Continuous Integration inte lagras i UI-gr\u00e4nssnittet i Jenkins, TeamCity och liknande, utan som textfiler direkt i projektets repository. Detta ger versionshantering. Det blir inte sv\u00e5rt att \u00e5terg\u00e5 eller bygga koden p\u00e5 en annan gren.<\/p>\n<p>Skript kan lagras i projektet. Men vad g\u00f6r man med milj\u00f6n?<\/p>\n<p><strong>Tips #3: Docker kan hj\u00e4lpa med milj\u00f6n.<\/strong><\/p>\n<p>Det kommer definitivt att hj\u00e4lpa Android-utvecklare, tyv\u00e4rr inte iOS.<\/p>\n<p>Detta \u00e4r ett exempel p\u00e5 en enkel dockerfil som inneh\u00e5ller jdk och android-sdk:<\/p>\n<pre><code class=\"plaintext\">FROM openjdk:8\n\nENV SDK_URL=\"https:\/\/dl.google.com\/android\/repository\/sdk-tools-linux-3859397.zip\" \n    ANDROID_HOME=\"\/usr\/local\/android-sdk\" \n    ANDROID_VERSION=26 \n    ANDROID_BUILD_TOOLS_VERSION=26.0.2\n\n# Ladda ner Android SDK\nRUN mkdir \"$ANDROID_HOME\" .android \n    &amp;&amp; cd \"$ANDROID_HOME\" \n    &amp;&amp; curl -o sdk.zip $SDK_URL \n    &amp;&amp; unzip sdk.zip \n    &amp;&amp; rm sdk.zip \n    &amp;&amp; yes | $ANDROID_HOME\/tools\/bin\/sdkmanager --licenses\n\n# Installera Android Build Tool och bibliotek\nRUN $ANDROID_HOME\/tools\/bin\/sdkmanager --update\nRUN $ANDROID_HOME\/tools\/bin\/sdkmanager \"build-tools;${ANDROID_BUILD_TOOLS_VERSION}\" \n    \"platforms;android-${ANDROID_VERSION}\" \n    \"platform-tools\"\n\nRUN mkdir \/application\nWORKDIR \/application\n<\/code><\/pre>\n<p>\nGenom att skriva denna dockerfil (jag s\u00e4ger i hemlighet att man kan hoppa \u00f6ver den och ladda ner en f\u00e4rdig fr\u00e5n GitHub) och bygga en bild f\u00e5r du en virtuell maskin d\u00e4r du kan bygga applikationen och k\u00f6ra Junit-tester.<\/p>\n<p>De tv\u00e5 huvudargumenten till varf\u00f6r detta \u00e4r meningsfullt \u00e4r skalbarhet och repetitivitet. Med docker kan man snabbt s\u00e4tta upp ett dussin byggagenter som har precis samma milj\u00f6 som den tidigare. Detta underl\u00e4ttar verkligen livet f\u00f6r CI-ingenj\u00f6rer. Att packa android-sdk i docker \u00e4r ganska enkelt, men med emulatorer \u00e4r det lite mer komplicerat: man f\u00e5r jobba lite mer (eller \u00e5terigen ladda ner en f\u00e4rdig fr\u00e5n GitHub).<\/p>\n<p><strong>Tips #4: Gl\u00f6m inte att tester g\u00f6rs inte f\u00f6r testernas skull, utan f\u00f6r m\u00e4nniskorna.<\/strong><\/p>\n<p>Utvecklare v\u00e4rdes\u00e4tter snabb och, framf\u00f6r allt, tydlig \u00e5terkoppling: vad som har g\u00e5tt fel, vilket test som misslyckades, var de kan se byggloggen.<\/p>\n<p><strong>Tips #5: Var pragmatisk i utvecklingen av Continuous Integration.<\/strong><\/p>\n<p>Ha en klar f\u00f6rst\u00e5else f\u00f6r vilken typ av fel du vill f\u00f6rebygga, hur mycket resurser, tid och maskintid du \u00e4r beredd att investera. F\u00f6r l\u00e5ngvariga tester kan man till exempel schemal\u00e4gga dem p\u00e5 natten. Och de tester som f\u00e5ngar mindre viktiga fel kan helt enkelt tas bort.<\/p>\n<p><strong>Tips #6: Anv\u00e4nd f\u00e4rdiga verktyg.<\/strong><\/p>\n<p>Det finns nu m\u00e5nga f\u00f6retag som erbjuder molnbaserad CI.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/e57aabd49ec01d14e83b64331fd84ec5.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nF\u00f6r sm\u00e5 team \u00e4r detta en bra l\u00f6sning. Du beh\u00f6ver inte underh\u00e5lla n\u00e5got, bara betala lite pengar, bygga din applikation och till och med k\u00f6ra instrumenteringstester.<\/p>\n<p><strong>Tips 7: i ett stort team \u00e4r in-house-l\u00f6sningar mer l\u00f6nsamma.<\/strong><\/p>\n<p>Men f\u00f6rr eller senare, med teamets tillv\u00e4xt, blir in-house-l\u00f6sningar mer f\u00f6rdelaktiga. Det finns en aspekt med dessa l\u00f6sningar. Inom ekonomin finns en lag om avtagande avkastning: i vilket projekt som helst blir varje efterf\u00f6ljande f\u00f6rb\u00e4ttring allt sv\u00e5rare och kr\u00e4ver allt mer investeringar.<\/p>\n<p>Ekonomin beskriver hela v\u00e5rt liv, inklusive Continuous Integration. Jag har byggt en graf \u00f6ver arbetsinsatsen f\u00f6r varje steg i utvecklingen av v\u00e5r Continuous Integration.<\/p>\n<p><img decoding=\"async\" alt=\"CI:s evolution inom mobilutvecklingsteam\" src=\"\/wp-content\/uploads\/2019\/04\/97bf8bff64f3ac9ae587fae195251da2.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDet \u00e4r tydligt att varje f\u00f6rb\u00e4ttring blir allt sv\u00e5rare. Genom att titta p\u00e5 denna graf kan man f\u00f6rst\u00e5 att utvecklingen av Continuous Integration m\u00e5ste ske i takt med att teamets storlek v\u00e4xer. F\u00f6r ett team p\u00e5 tv\u00e5 personer \u00e4r det inte s\u00e5 bra id\u00e9 att spendera 50 dagar p\u00e5 att utveckla en intern emulatorfarm. Men samtidigt \u00e4r det ocks\u00e5 en d\u00e5lig id\u00e9 f\u00f6r ett stort team att helt l\u00e4mna Continuous Integration \u00e5t sidan, eftersom mer tid kommer att g\u00e5 \u00e5t till integrationsproblem, att fixa kommunikation etc.<\/p>\n<p>Vi b\u00f6rjade med att automation \u00e4r n\u00f6dv\u00e4ndig eftersom m\u00e4nniskor \u00e4r dyra, de g\u00f6r misstag och \u00e4r lata. Men de som automatiserar \u00e4r ocks\u00e5 m\u00e4nniskor. D\u00e4rf\u00f6r g\u00e4ller samma problem \u00e4ven f\u00f6r automation.<\/p>\n<ul>\n<li>Att automatisera \u00e4r dyrt. T\u00e4nk p\u00e5 grafen \u00f6ver arbetsinsatsen.<\/li>\n<li>Vid automation g\u00f6r m\u00e4nniskor misstag.<\/li>\n<li>Att automatisera \u00e4r ibland v\u00e4ldigt tr\u00e5kigt, eftersom allt redan fungerar. Varf\u00f6r f\u00f6rb\u00e4ttra n\u00e5got, varf\u00f6r all denna Continuous Integration?<\/li>\n<\/ul>\n<p>\nMen jag har statistik: 20% av byggen f\u00e5ngar buggar. Och detta h\u00e4nder inte f\u00f6r att v\u00e5ra utvecklare skriver d\u00e5lig kod. Det beror p\u00e5 att utvecklarna \u00e4r s\u00e4kra p\u00e5 att om de g\u00f6r n\u00e5got misstag, kommer det inte att hamna i utvecklingsgrenen, det kommer att uppt\u00e4ckas av automatiserade tester. D\u00e4rf\u00f6r kan utvecklarna l\u00e4gga mer tid p\u00e5 att skriva kod och intressanta saker, ist\u00e4llet f\u00f6r att k\u00f6ra och kontrollera n\u00e5got lokalt.<\/p>\n<p><strong>Syssla med Continuous Integration. Men i m\u00e5ttliga m\u00e4ngder.<\/strong><\/p>\n<blockquote><p>F\u00f6r \u00f6vrigt g\u00f6r Nikolay Nesterov inte bara fantastiska f\u00f6redrag sj\u00e4lv, utan ing\u00e5r ocks\u00e5 i programkommitt\u00e9n <noindex><a rel=\"nofollow\" href=\"https:\/\/appsconf.ru\/moscow\/2019\">AppsConf<\/a><\/noindex> och hj\u00e4lper andra att f\u00f6rbereda meningsfulla presentationer f\u00f6r er. Hela programmet f\u00f6r n\u00e4sta konferens kan bed\u00f6mas utifr\u00e5n \u00e4mnena i <noindex><a rel=\"nofollow\" href=\"https:\/\/appsconf.ru\/moscow\/2019\/schedule\">schemat<\/a><\/noindex>. F\u00f6r mer information, kom till Infoprime den 22-23 april.<\/p><\/blockquote>\n<p>K\u00e4lla: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/oleg-bunin\/blog\/447608\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u0445. \u0423\u0441\u043b\u043e\u0432\u0438\u044f \u0443\u0441\u043f\u0435\u0445\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u0445\u0435\u043c\u044b. \u041d\u0430\u043f\u0438\u0441\u0430\u0432 \u043a\u043e\u0434, \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u043e\u043d: \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043b\u043e\u043c\u0430\u0435\u0442, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u043f\u0438\u0441\u0430\u043b\u0438 \u0432\u0430\u0448\u0438 \u043a\u043e\u043b\u043b\u0435\u0433\u0438. \u0415\u0441\u043b\u0438 \u043e\u0431\u0430 \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f, \u0442\u043e \u0432\u044b \u043d\u0430 \u043f\u0443\u0442\u0438 \u043a \u0443\u0441\u043f\u0435\u0445\u0443. \u0427\u0442\u043e\u0431\u044b \u043b\u0435\u0433\u043a\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u044d\u0442\u0438 \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u0438 \u043d\u0435 \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0441 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":23270,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-31304","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.4 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u0445.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/sv\/blog\/administrirovanie\/evolyutsiya-ci-v-komande-mobilnoj-razrabotki\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sv_SE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u042d\u0432\u043e\u043b\u044e\u0446\u0438\u044f CI \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u0445.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/sv\/blog\/administrirovanie\/evolyutsiya-ci-v-komande-mobilnoj-razrabotki\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:40:34+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:40:34+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Evolutionen av CI i mobilutvecklingsteamet | ProHoster","description":"I dagens l\u00e4ge utvecklas de flesta programvaruprodukter i team.","canonical_url":"https:\/\/prohoster.info\/sv\/blog\/administrirovanie\/evolyutsiya-ci-v-komande-mobilnoj-razrabotki","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sv_SE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u042d\u0432\u043e\u043b\u044e\u0446\u0438\u044f CI \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 | ProHoster","og:description":"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u0445.","og:url":"https:\/\/prohoster.info\/sv\/blog\/administrirovanie\/evolyutsiya-ci-v-komande-mobilnoj-razrabotki","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:40:34+00:00","article:modified_time":"2019-10-31T18:40:34+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"31304","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 05:30:21","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 03:19:49","updated":"2026-01-21 05:30:21","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/31304","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/comments?post=31304"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/31304\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media\/23270"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media?parent=31304"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/categories?post=31304"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/tags?post=31304"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}