วิวัฒนาการของ CI ในทีมพัฒนามือถือ

ปัจจุบันผลิตภัณฑ์ซอฟต์แวร์ส่วนใหญ่ได้รับการพัฒนาเป็นทีม เงื่อนไขสำหรับการพัฒนาทีมที่ประสบความสำเร็จสามารถแสดงได้ในรูปแบบไดอะแกรมง่ายๆ

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

เมื่อคุณเขียนโค้ดแล้ว คุณต้องแน่ใจว่า:

  1. Работает
  2. มันไม่ได้ทำลายอะไรเลย รวมถึงโค้ดที่เพื่อนร่วมงานของคุณเขียนด้วย

หากตรงตามเงื่อนไขทั้งสองข้อ แสดงว่าคุณอยู่บนเส้นทางสู่ความสำเร็จ เพื่อให้ตรวจสอบเงื่อนไขเหล่านี้ได้อย่างง่ายดายและไม่เบี่ยงเบนไปจากเส้นทางที่ทำกำไร เราจึงได้คิดค้นการผสานรวมอย่างต่อเนื่อง

CI คือขั้นตอนการทำงานที่คุณรวมโค้ดของคุณเข้ากับโค้ดผลิตภัณฑ์โดยรวมให้บ่อยที่สุดเท่าที่จะเป็นไปได้ และคุณไม่เพียงแค่บูรณาการ แต่ยังตรวจสอบอย่างต่อเนื่องว่าทุกอย่างทำงาน เนื่องจากคุณจำเป็นต้องตรวจสอบบ่อยครั้ง จึงคุ้มค่าที่จะคิดถึงระบบอัตโนมัติ คุณสามารถตรวจสอบทุกอย่างได้ด้วยตนเอง แต่คุณไม่ควรตรวจสอบ และนี่คือเหตุผล

  • เรียนทุกท่าน. ชั่วโมงการทำงานของโปรแกรมเมอร์นั้นแพงกว่าการทำงานของเซิร์ฟเวอร์หนึ่งชั่วโมง
  • ผู้คนทำผิดพลาด. ดังนั้น สถานการณ์อาจเกิดขึ้นเมื่อรันการทดสอบในสาขาที่ไม่ถูกต้อง หรือมีการรวบรวมการคอมมิตที่ไม่ถูกต้องสำหรับผู้ทดสอบ
  • คนก็ขี้เกียจ. บางครั้งเมื่อฉันทำงานเสร็จก็เกิดความคิด: “มีอะไรให้ตรวจสอบ? ฉันเขียนสองบรรทัด - ทุกอย่างใช้งานได้! ฉันคิดว่าบางท่านก็มีความคิดเช่นนั้นเช่นกัน แต่คุณควรตรวจสอบอยู่เสมอ

วิธีการบูรณาการอย่างต่อเนื่องถูกนำไปใช้และพัฒนาในทีมพัฒนามือถือ Avito วิธีที่พวกเขาเพิ่มจาก 0 ถึง 450 บิลด์ต่อวัน และเครื่องจักรที่สร้างนั้นประกอบได้ 200 ชั่วโมงต่อวัน Nikolai Nesterov กล่าว (เนสเตอรอฟ) เป็นผู้มีส่วนร่วมในการเปลี่ยนแปลงเชิงวิวัฒนาการทั้งหมดของแอปพลิเคชัน CI/CD Android

เรื่องราวอิงจากตัวอย่างคำสั่ง Android แต่แนวทางส่วนใหญ่ก็ใช้ได้กับ iOS เช่นกัน


กาลครั้งหนึ่ง มีคนคนหนึ่งทำงานในทีม Avito Android ตามคำจำกัดความ เขาไม่ต้องการอะไรจากการบูรณาการอย่างต่อเนื่อง: ไม่มีใครที่จะบูรณาการด้วย

แต่แอปพลิเคชั่นก็เพิ่มขึ้น มีงานใหม่ ๆ ปรากฏขึ้นมากขึ้นเรื่อย ๆ และทีมก็เติบโตขึ้นตามไปด้วย เมื่อถึงจุดหนึ่ง ถึงเวลาที่จะต้องสร้างกระบวนการรวมโค้ดอย่างเป็นทางการมากขึ้น มีการตัดสินใจที่จะใช้ Git flow

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

แนวคิดของ Git flow เป็นที่รู้จักกันดี: โปรเจ็กต์มีสาขาการพัฒนาร่วมกันเพียงสาขาเดียว และสำหรับฟีเจอร์ใหม่แต่ละรายการ นักพัฒนาจะตัดสาขาแยกกัน คอมมิตกับมัน พุช และเมื่อพวกเขาต้องการรวมโค้ดของตนเข้ากับสาขาพัฒนา ให้เปิด ดึงคำขอ เพื่อแบ่งปันความรู้และหารือเกี่ยวกับแนวทางต่างๆ เราได้แนะนำการตรวจสอบโค้ด กล่าวคือ เพื่อนร่วมงานจะต้องตรวจสอบและยืนยันโค้ดของกันและกัน

การตรวจสอบ

การเห็นรหัสด้วยตาของคุณนั้นเจ๋ง แต่ก็ไม่เพียงพอ ดังนั้นจึงมีการนำการตรวจสอบอัตโนมัติมาใช้

  • ก่อนอื่นเราตรวจสอบ การประกอบ ARK.
  • เป็นจำนวนมาก การทดสอบ Junit.
  • เราพิจารณาความครอบคลุมของโค้ดเนื่องจากเรากำลังดำเนินการทดสอบ

เพื่อทำความเข้าใจว่าควรดำเนินการตรวจสอบเหล่านี้อย่างไร มาดูกระบวนการพัฒนาใน Avito กัน

สามารถแสดงเป็นแผนผังได้ดังนี้:

  • นักพัฒนาเขียนโค้ดบนแล็ปท็อปของเขา คุณสามารถเรียกใช้การตรวจสอบการรวมได้ที่นี่ ไม่ว่าจะด้วย Commit Hook หรือเพียงแค่เรียกใช้การตรวจสอบในเบื้องหลัง
  • หลังจากที่นักพัฒนาได้พุชโค้ดแล้ว เขาจะเปิดคำขอดึง เพื่อให้โค้ดรวมอยู่ในสาขาที่พัฒนาแล้ว จำเป็นต้องผ่านการตรวจสอบโค้ดและรวบรวมการยืนยันตามจำนวนที่ต้องการ คุณสามารถเปิดใช้งานการตรวจสอบและบิลด์ได้ที่นี่: จนกว่าการสร้างทั้งหมดจะสำเร็จ คำขอดึงข้อมูลจะไม่สามารถรวมเข้าด้วยกันได้
  • หลังจากรวมคำขอดึงข้อมูลและโค้ดรวมอยู่ในการพัฒนาแล้ว คุณสามารถเลือกเวลาที่สะดวกได้ เช่น ในเวลากลางคืน ซึ่งเป็นเวลาที่เซิร์ฟเวอร์ทั้งหมดว่าง และดำเนินการตรวจสอบได้มากเท่าที่คุณต้องการ

ไม่มีใครชอบการสแกนบนแล็ปท็อปของตน เมื่อนักพัฒนาเสร็จสิ้นคุณสมบัติ เขาต้องการที่จะพุชมันอย่างรวดเร็วและเปิดคำขอดึง หากในขณะนี้มีการเปิดตัวการตรวจสอบที่ยาวนาน สิ่งนี้ไม่เพียงแต่ไม่น่าพอใจเท่านั้น แต่ยังทำให้การพัฒนาช้าลงด้วย: ในขณะที่แล็ปท็อปกำลังตรวจสอบบางสิ่งบางอย่าง มันเป็นไปไม่ได้ที่จะทำงานได้ตามปกติ

เราชอบดำเนินการตรวจสอบในเวลากลางคืนมาก เนื่องจากมีเวลาและเซิร์ฟเวอร์จำนวนมาก คุณสามารถท่องไปรอบๆ ได้ แต่น่าเสียดายที่เมื่อโค้ดคุณลักษณะได้รับการพัฒนา นักพัฒนาก็มีแรงจูงใจน้อยลงมากในการแก้ไขข้อผิดพลาดที่ CI พบ ฉันคิดอยู่เป็นระยะเมื่อดูข้อผิดพลาดทั้งหมดที่พบในรายงานตอนเช้าว่าฉันจะแก้ไขมันในสักวันหนึ่งในภายหลัง เพราะตอนนี้มีงานใหม่ที่ยอดเยี่ยมในจิราที่ฉันแค่อยากจะเริ่มทำ

หากเช็คขัดขวางคำขอดึง แสดงว่ามีแรงจูงใจเพียงพอ เนื่องจากโค้ดจะไม่เข้าสู่การพัฒนาจนกว่าบิลด์จะเปลี่ยนเป็นสีเขียว ซึ่งหมายความว่างานจะไม่เสร็จสมบูรณ์

ด้วยเหตุนี้ เราจึงเลือกกลยุทธ์ต่อไปนี้: เราดำเนินการตรวจสอบชุดที่เป็นไปได้สูงสุดในเวลากลางคืน และเปิดตัวชุดการตรวจสอบที่สำคัญที่สุด และที่สำคัญที่สุดคือชุดการตรวจสอบที่เร็วที่สุดสำหรับคำขอดึง แต่เราไม่ได้หยุดอยู่แค่นั้น ในทางกลับกัน เราจะปรับความเร็วของการตรวจสอบให้เหมาะสม เพื่อถ่ายโอนการตรวจสอบจากโหมดกลางคืนเพื่อดึงการตรวจสอบคำขอ

ในขณะนั้น การสร้างทั้งหมดของเราเสร็จสมบูรณ์อย่างรวดเร็ว ดังนั้นเราจึงรวม ARK บิลด์ การทดสอบ Junit และการคำนวณการครอบคลุมโค้ดไว้เป็นตัวบล็อกสำหรับคำขอดึง เราเปิดใช้งาน คิดเกี่ยวกับมัน และละทิ้งการครอบคลุมโค้ดเพราะเราคิดว่าเราไม่ต้องการมัน

เราใช้เวลาสองวันในการตั้งค่า CI พื้นฐานให้เสร็จสมบูรณ์ (ต่อไปนี้จะเป็นเวลาโดยประมาณ ซึ่งจำเป็นสำหรับการปรับขนาด)

หลังจากนั้นเราก็เริ่มคิดเพิ่มเติม - เราตรวจสอบถูกต้องแล้วหรือยัง? เรากำลังรัน builds ตามคำขอ pull อย่างถูกต้องหรือไม่?

เราเริ่มต้นการสร้างการคอมมิตครั้งสุดท้ายของสาขาที่เปิดการร้องขอการดึง แต่การทดสอบการคอมมิตนี้สามารถแสดงได้ว่าโค้ดที่นักพัฒนาเขียนนั้นใช้งานได้เท่านั้น แต่พวกเขาไม่ได้พิสูจน์ว่าเขาไม่ได้ทำลายอะไรเลย ที่จริงแล้ว คุณจำเป็นต้องตรวจสอบสถานะของสาขาที่กำลังพัฒนาหลังจากรวมฟีเจอร์เข้ากับฟีเจอร์แล้ว

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

เพื่อทำเช่นนี้ เราได้เขียนสคริปต์ทุบตีแบบง่ายๆ premerge.sh:

#!/usr/bin/env bash

set -e

git fetch origin develop

git merge origin/develop

ที่นี่การเปลี่ยนแปลงล่าสุดทั้งหมดจากการพัฒนาจะถูกดึงขึ้นมาและรวมเข้ากับสาขาปัจจุบัน เราได้เพิ่มสคริปต์ premerge.sh เป็นขั้นตอนแรกในบิวด์ทั้งหมด และเริ่มตรวจสอบสิ่งที่เราต้องการอย่างแน่นอน บูรณาการ.

ใช้เวลาสามวันในการแปลปัญหา ค้นหาวิธีแก้ไข และเขียนสคริปต์นี้

แอปพลิเคชันได้รับการพัฒนา มีงานปรากฏขึ้นมากขึ้นเรื่อยๆ ทีมก็เติบโตขึ้น และบางครั้ง premerge.sh ก็เริ่มทำให้เราผิดหวัง การพัฒนามีการเปลี่ยนแปลงที่ขัดแย้งกันซึ่งทำให้โครงสร้างพัง

ตัวอย่างเหตุการณ์นี้เกิดขึ้น:

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

นักพัฒนาสองคนเริ่มทำงานกับคุณสมบัติ A และ B พร้อมกัน ผู้พัฒนาคุณสมบัติ A ค้นพบคุณสมบัติที่ไม่ได้ใช้ในโครงการ answer() และก็เอามันออกไปเหมือนลูกเสือที่ดี ในเวลาเดียวกัน ผู้พัฒนาฟีเจอร์ B เพิ่มการเรียกใหม่ให้กับฟังก์ชันนี้ในสาขาของเขา

นักพัฒนาทำงานให้เสร็จและเปิดคำขอดึงในเวลาเดียวกัน เปิดตัว builds แล้ว premerge.sh จะตรวจสอบคำขอดึงทั้งสองคำขอที่เกี่ยวข้องกับสถานะการพัฒนาล่าสุด - การตรวจสอบทั้งหมดจะเป็นสีเขียว หลังจากนั้น คำขอการดึงของฟีเจอร์ A จะถูกรวมเข้าด้วยกัน คำขอการดึงของฟีเจอร์ B จะถูกรวมเข้าด้วยกัน... บูม! ตัวแบ่งการพัฒนาเนื่องจากโค้ดการพัฒนามีการเรียกไปยังฟังก์ชันที่ไม่มีอยู่จริง

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

เมื่อมันไม่พัฒนาก็เป็น ภัยพิบัติในท้องถิ่น. ทั้งทีมไม่สามารถรวบรวมสิ่งใดและส่งไปทดสอบได้

มันบังเอิญว่าฉันทำงานเกี่ยวกับโครงสร้างพื้นฐานบ่อยที่สุด: การวิเคราะห์ เครือข่าย ฐานข้อมูล นั่นคือฉันเองที่เขียนฟังก์ชันและคลาสเหล่านั้นที่นักพัฒนารายอื่นใช้ ด้วยเหตุนี้ฉันจึงพบว่าตัวเองตกอยู่ในสถานการณ์ที่คล้ายกันบ่อยมาก ฉันยังมีภาพนี้แขวนอยู่สักพักหนึ่ง

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

เนื่องจากสิ่งนี้ไม่เหมาะกับเรา เราจึงเริ่มสำรวจทางเลือกต่างๆ เกี่ยวกับวิธีการป้องกัน

วิธีที่จะไม่พังพัฒนา

ตัวเลือกแรก: สร้างคำขอดึงทั้งหมดใหม่เมื่ออัปเดตการพัฒนา ในตัวอย่างของเรา หากคำขอการดึงที่มีคุณลักษณะ A เป็นคำขอแรกที่จะรวมในการพัฒนา คำขอการดึงของคุณลักษณะ B จะถูกสร้างขึ้นใหม่ และด้วยเหตุนี้ การตรวจสอบจะล้มเหลวเนื่องจากข้อผิดพลาดในการคอมไพล์

เพื่อให้เข้าใจว่าจะใช้เวลานานแค่ไหน ลองพิจารณาตัวอย่างที่มี PR สองตัว เราเปิด PR สองรายการ: สองบิลด์, การตรวจสอบสองครั้ง หลังจากที่ PR แรกถูกรวมเข้ากับการพัฒนาแล้ว PR ที่สองจะต้องถูกสร้างใหม่ โดยรวมแล้ว PR สองตัวต้องมีการตรวจสอบสามครั้ง: 2 + 1 = 3

โดยหลักการแล้วก็โอเค แต่เราดูที่สถิติ และสถานการณ์ทั่วไปในทีมของเราคือ 10 PR ที่เปิดอยู่ จากนั้นจำนวนการตรวจสอบคือผลรวมของความก้าวหน้า: 10 + 9 +... + 1 = 55 นั่นคือยอมรับ 10 ประชาสัมพันธ์ คุณต้องสร้างใหม่ 55 ครั้ง และนี่คือสถานการณ์ในอุดมคติ เมื่อการตรวจสอบทั้งหมดผ่านในครั้งแรก เมื่อไม่มีใครเปิดคำขอดึงเพิ่มเติมในขณะที่โหลเหล่านี้กำลังประมวลผล

ลองนึกภาพตัวเองเป็นนักพัฒนาที่ต้องเป็นคนแรกที่คลิกที่ปุ่ม "รวม" เพราะหากเพื่อนบ้านทำเช่นนี้ คุณจะต้องรอจนกว่างานสร้างทั้งหมดจะดำเนินไปอีกครั้ง... ไม่ นั่นจะไม่ทำงาน จะทำให้การพัฒนาช้าลงอย่างมาก

วิธีที่สองที่เป็นไปได้: รวบรวมคำขอดึงหลังจากตรวจสอบโค้ด นั่นคือ คุณเปิดคำขอดึงข้อมูล รวบรวมการอนุมัติตามจำนวนที่ต้องการจากเพื่อนร่วมงาน แก้ไขสิ่งที่จำเป็น จากนั้นเปิดใช้งานรุ่น หากสำเร็จ คำขอดึงจะถูกรวมเข้ากับการพัฒนา ในกรณีนี้ จะไม่มีการรีสตาร์ทเพิ่มเติม แต่การตอบสนองจะช้าลงอย่างมาก ในฐานะนักพัฒนา เมื่อฉันเปิดคำขอดึงข้อมูล ฉันต้องการดูว่ามันจะใช้งานได้ทันทีหรือไม่ ตัวอย่างเช่น หากการทดสอบล้มเหลว คุณจะต้องแก้ไขอย่างรวดเร็ว ในกรณีของการสร้างล่าช้า ผลตอบรับจะช้าลง และทำให้การพัฒนาทั้งหมดลดลง สิ่งนี้ก็ไม่เหมาะกับเราเช่นกัน

เป็นผลให้เหลือเพียงตัวเลือกที่สามเท่านั้น - จักรยาน. รหัสทั้งหมดของเรา แหล่งที่มาทั้งหมดของเราถูกจัดเก็บไว้ในพื้นที่เก็บข้อมูลบนเซิร์ฟเวอร์ Bitbucket ดังนั้นเราจึงต้องพัฒนาปลั๊กอินสำหรับ Bitbucket

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

ปลั๊กอินนี้จะแทนที่กลไกการรวมคำขอดึง จุดเริ่มต้นคือมาตรฐาน: PR เปิดขึ้น แอสเซมบลีทั้งหมดถูกเปิดใช้งาน การตรวจสอบโค้ดเสร็จสมบูรณ์ แต่หลังจากการตรวจสอบโค้ดเสร็จสิ้นและนักพัฒนาตัดสินใจที่จะคลิกที่ "ผสาน" ปลั๊กอินจะตรวจสอบสถานะการพัฒนาที่ดำเนินการตรวจสอบ หากการพัฒนาได้รับการอัปเดตหลังจากสร้างแล้ว ปลั๊กอินจะไม่อนุญาตให้รวมคำขอดึงดังกล่าวเข้ากับสาขาหลัก มันจะรีสตาร์ทบิวด์ของการพัฒนาที่ค่อนข้างล่าสุดอีกครั้ง

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

ในตัวอย่างของเราที่มีการเปลี่ยนแปลงที่ขัดแย้งกัน บิลด์ดังกล่าวจะล้มเหลวเนื่องจากข้อผิดพลาดในการคอมไพล์ ดังนั้น ผู้พัฒนาฟีเจอร์ B จะต้องแก้ไขโค้ด เริ่มการตรวจสอบใหม่ จากนั้นปลั๊กอินจะใช้คำขอดึงโดยอัตโนมัติ

ก่อนที่จะใช้ปลั๊กอินนี้ เราใช้เวลาตรวจสอบเฉลี่ย 2,7 ครั้งต่อคำขอดึงข้อมูล ด้วยปลั๊กอินมีการเปิดตัว 3,6 สิ่งนี้เหมาะกับเรา

เป็นที่น่าสังเกตว่าปลั๊กอินนี้มีข้อเสียเปรียบ: รีสตาร์ทบิลด์เพียงครั้งเดียวเท่านั้น นั่นคือยังคงมีหน้าต่างเล็ก ๆ ที่สามารถพัฒนาการเปลี่ยนแปลงที่ขัดแย้งกันได้ แต่โอกาสที่จะเกิดสิ่งนี้มีน้อย และเราได้ทำการแลกเปลี่ยนระหว่างจำนวนการออกสตาร์ทและความน่าจะเป็นที่จะล้มเหลว ในสองปีมันถูกยิงเพียงครั้งเดียว ดังนั้นมันคงไม่สูญเปล่า

เราใช้เวลาสองสัปดาห์ในการเขียนปลั๊กอิน Bitbucket เวอร์ชันแรก

เช็คใหม่

ในขณะเดียวกันทีมของเราก็ยังคงเติบโตอย่างต่อเนื่อง มีการเพิ่มเช็คใหม่แล้ว

เราคิดว่า: ทำไมต้องทำผิดพลาดหากสามารถป้องกันได้? และนั่นคือเหตุผลที่พวกเขาดำเนินการ การวิเคราะห์โค้ดแบบคงที่. เราเริ่มต้นด้วย Lint ซึ่งรวมอยู่ใน Android SDK แต่ในเวลานั้นเขาไม่รู้วิธีทำงานกับโค้ด Kotlin เลย และเรามีแอปพลิเคชันที่เขียนด้วย Kotlin ไปแล้ว 75% ดังนั้นจึงมีการเพิ่มสิ่งในตัวเข้ากับผ้าสำลี การตรวจสอบ Android Studio

เพื่อทำเช่นนี้ เราต้องทำการกระทำในทางที่ผิดมากมาย: นำ Android Studio มาบรรจุใน Docker และรันบน CI ด้วยจอภาพเสมือน เพื่อให้คิดว่ามันทำงานบนแล็ปท็อปจริง แต่มันก็ได้ผล

ในช่วงเวลานี้เองที่เราเริ่มเขียนกันมากมาย การทดสอบเครื่องมือวัด และนำไปปฏิบัติ การทดสอบภาพหน้าจอ. นี่คือเมื่อมีการสร้างภาพหน้าจออ้างอิงสำหรับมุมมองขนาดเล็กแยกต่างหาก และการทดสอบประกอบด้วยการถ่ายภาพหน้าจอจากมุมมองและเปรียบเทียบกับมาตรฐานโดยตรงแบบพิกเซลต่อพิกเซล หากมีความคลาดเคลื่อน แสดงว่าเลย์เอาต์มีข้อผิดพลาดไปที่ไหนสักแห่งหรือมีบางอย่างผิดปกติในรูปแบบ

แต่การทดสอบเครื่องมือและการทดสอบภาพหน้าจอจำเป็นต้องดำเนินการบนอุปกรณ์: บนเครื่องจำลองหรือบนอุปกรณ์จริง เมื่อพิจารณาว่ามีการทดสอบจำนวนมากและมีการดำเนินการบ่อยครั้ง จึงจำเป็นต้องมีทั้งฟาร์ม การเริ่มต้นฟาร์มของคุณเองนั้นใช้แรงงานคนมากเกินไป ดังนั้นเราจึงพบตัวเลือกสำเร็จรูป - Firebase Test Lab

ห้องปฏิบัติการทดสอบฐานไฟ

ถูกเลือกเนื่องจาก Firebase เป็นผลิตภัณฑ์ของ Google ซึ่งหมายความว่าควรเชื่อถือได้และไม่น่าจะตายไป ราคาสมเหตุสมผล: $5 ต่อชั่วโมงในการใช้งานอุปกรณ์จริง และ 1 $ ต่อชั่วโมงสำหรับการทำงานของโปรแกรมจำลอง

ใช้เวลาประมาณสามสัปดาห์ในการปรับใช้ Firebase Test Lab ใน CI ของเรา

แต่ทีมยังคงเติบโตอย่างต่อเนื่อง และ Firebase ก็เริ่มทำให้เราผิดหวัง ในเวลานั้นเขาไม่มี SLA ใด ๆ บางครั้ง Firebase ทำให้เรารอจนกว่าอุปกรณ์ตามจำนวนที่กำหนดจะว่างสำหรับการทดสอบ และไม่ได้เริ่มดำเนินการทันทีตามที่เราต้องการ การรอคิวใช้เวลานานถึงครึ่งชั่วโมงซึ่งถือว่านานมาก การทดสอบการใช้เครื่องมือดำเนินการกับ PR ทุกรายการ ความล่าช้าทำให้การพัฒนาช้าลงอย่างมาก จากนั้นการเรียกเก็บเงินรายเดือนก็มาพร้อมกับผลรวมแบบกลม โดยทั่วไปแล้ว มีการตัดสินใจที่จะละทิ้ง Firebase และทำงานภายในองค์กร เนื่องจากทีมเติบโตขึ้นมามากพอแล้ว

นักเทียบท่า + Python + ทุบตี

เรานำ Docker ยัดอีมูเลเตอร์ลงไป เขียนโปรแกรมง่ายๆ ใน Python ซึ่งในเวลาที่เหมาะสมจะแสดงจำนวนอีมูเลเตอร์ที่ต้องการในเวอร์ชันที่ต้องการและหยุดเมื่อจำเป็น และแน่นอนว่ามีสคริปต์ทุบตีสองสามตัว - เราจะอยู่ที่ไหนถ้าไม่มีพวกมัน?

ใช้เวลาห้าสัปดาห์ในการสร้างสภาพแวดล้อมการทดสอบของเราเอง

ด้วยเหตุนี้ สำหรับทุกคำขอดึงจึงมีรายการตรวจสอบที่บล็อกการรวมจำนวนมาก:

  • การประกอบ ARK;
  • การทดสอบ Junit;
  • ผ้าสำลี;
  • การตรวจสอบ Android Studio;
  • การทดสอบเครื่องมือวัด
  • การทดสอบภาพหน้าจอ

สิ่งนี้ช่วยป้องกันการพังที่อาจเกิดขึ้นได้มากมาย ในทางเทคนิคแล้วทุกอย่างทำงานได้ แต่นักพัฒนาบ่นว่าการรอผลลัพธ์นานเกินไป

นานแค่ไหนก็นานเกินไป? เราอัปโหลดข้อมูลจาก Bitbucket และ TeamCity ไปยังระบบการวิเคราะห์และตระหนักว่า เวลารอโดยเฉลี่ย 45 นาที. กล่าวคือ นักพัฒนาเมื่อเปิดคำขอดึง จะรอประมาณ 45 นาทีโดยเฉลี่ยเพื่อให้ได้ผลลัพธ์ของบิลด์ ในความคิดของฉัน มันเยอะมาก และคุณไม่สามารถทำงานแบบนั้นได้

แน่นอนว่าเราตัดสินใจเร่งการสร้างทั้งหมดของเรา

มาเร่งความเร็วกันเถอะ

เมื่อเห็นว่าบิลด์มักจะยืนต่อคิว สิ่งแรกที่เราทำคือ ซื้อฮาร์ดแวร์เพิ่มเติม — การพัฒนาอย่างกว้างขวางนั้นง่ายที่สุด บิลด์หยุดการรอคิว แต่เวลาในการรอลดลงเพียงเล็กน้อย เนื่องจากการตรวจสอบบางอย่างใช้เวลานานมาก

การถอดเช็คที่ใช้เวลานานเกินไป

การบูรณาการอย่างต่อเนื่องของเราสามารถตรวจจับข้อผิดพลาดและปัญหาประเภทนี้ได้

  • จะไม่ไป. CI สามารถตรวจจับข้อผิดพลาดในการคอมไพล์เมื่อมีบางสิ่งไม่ถูกสร้างขึ้นเนื่องจากการเปลี่ยนแปลงที่ขัดแย้งกัน อย่างที่ผมบอกไปแล้วว่าไม่มีใครสามารถรวบรวมอะไรได้เลย การพัฒนาหยุดลง และทุกคนก็วิตกกังวล
  • ข้อผิดพลาดในพฤติกรรม. ตัวอย่างเช่น เมื่อแอปพลิเคชันถูกสร้างขึ้น แต่หยุดทำงานเมื่อคุณกดปุ่ม หรือไม่ได้กดปุ่มเลย สิ่งนี้ไม่ดีเพราะข้อผิดพลาดดังกล่าวสามารถเข้าถึงผู้ใช้ได้
  • ข้อผิดพลาดในเค้าโครง. ตัวอย่างเช่น มีการคลิกปุ่ม แต่ได้ย้ายไปทางซ้าย 10 พิกเซล
  • หนี้ทางเทคนิคเพิ่มขึ้น.

หลังจากดูรายการนี้แล้ว เราก็พบว่ามีเพียงสองประเด็นแรกเท่านั้นที่มีความสำคัญ เราต้องการจับปัญหาดังกล่าวก่อน จุดบกพร่องในเลย์เอาต์จะถูกค้นพบในขั้นตอนการตรวจสอบการออกแบบ และสามารถแก้ไขได้อย่างง่ายดาย การจัดการกับหนี้ทางเทคนิคต้องมีกระบวนการและการวางแผนที่แยกจากกัน ดังนั้นเราจึงตัดสินใจที่จะไม่ทดสอบตามคำขอดึง

จากการจำแนกประเภทนี้ เราได้เขย่ารายการเช็คทั้งหมด ขีดฆ่าผ้าสำลี และเลื่อนการเปิดตัวข้ามคืนเพียงเพื่อจะได้รายงานจำนวนปัญหาในโครงการ เราตกลงที่จะทำงานแยกกันกับหนี้ทางเทคนิคและ การตรวจสอบ Android Studio ถูกยกเลิกโดยสิ้นเชิง. Android Studio ใน Docker สำหรับการตรวจสอบการทำงานฟังดูน่าสนใจ แต่ทำให้เกิดปัญหามากมายในการสนับสนุน การอัปเดตเวอร์ชัน Android Studio หมายถึงการต่อสู้กับข้อบกพร่องที่เข้าใจยาก นอกจากนี้ยังเป็นเรื่องยากที่จะสนับสนุนการทดสอบภาพหน้าจอ เนื่องจากไลบรารีไม่เสถียรมากนักและมีผลบวกลวง การทดสอบภาพหน้าจอถูกลบออกจากรายการตรวจสอบแล้ว.

เป็นผลให้เราเหลือ:

  • การประกอบ ARK;
  • การทดสอบ Junit;
  • การทดสอบเครื่องมือวัด

แคชระยะไกล Gradle

หากไม่มีการตรวจสอบอย่างหนักทุกอย่างก็ดีขึ้น แต่ความสมบูรณ์แบบไม่มีขีดจำกัด!

แอปพลิเคชันของเราแบ่งออกเป็นโมดูลการไล่ระดับประมาณ 150 โมดูลแล้ว โดยปกติแล้วแคชระยะไกลของ Gradle จะทำงานได้ดีในกรณีนี้ ดังนั้นเราจึงตัดสินใจลองใช้

Gradle Remote Cache เป็นบริการที่สามารถแคชการสร้างสิ่งประดิษฐ์สำหรับงานแต่ละงานในแต่ละโมดูล Gradle แทนที่จะรวบรวมโค้ดจริงๆ ให้ใช้ HTTP เพื่อเคาะแคชระยะไกลและถามว่ามีใครทำงานนี้แล้วหรือยัง ถ้าใช่ มันก็เพียงดาวน์โหลดผลลัพธ์

การเรียกใช้แคชระยะไกลของ Gradle เป็นเรื่องง่ายเนื่องจาก Gradle มีอิมเมจ Docker เราสามารถทำได้ภายในสามชั่วโมง

สิ่งที่คุณต้องทำคือเปิด Docker และเขียนหนึ่งบรรทัดในโปรเจ็กต์ แม้ว่าจะสามารถเปิดตัวได้อย่างรวดเร็ว แต่ก็ต้องใช้เวลาค่อนข้างนานเพื่อให้ทุกอย่างทำงานได้ดี

ด้านล่างนี้คือกราฟแคชที่หายไป

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

ในช่วงเริ่มต้น เปอร์เซ็นต์ของแคชที่หายไปคือประมาณ 65 หลังจากผ่านไปสามสัปดาห์ เราก็สามารถเพิ่มค่านี้เป็น 20% ได้ ปรากฎว่างานที่แอปพลิเคชัน Android รวบรวมมีการพึ่งพาสกรรมกริยาแปลก ๆ เนื่องจาก Gradle พลาดแคช

ด้วยการเชื่อมต่อแคช เราจึงเร่งความเร็วในการสร้างได้อย่างมาก แต่นอกจากการประกอบแล้ว ยังมีการทดสอบเครื่องมือวัดด้วยซึ่งใช้เวลานาน บางทีอาจไม่จำเป็นต้องรันการทดสอบทั้งหมดสำหรับคำขอดึงทุกครั้ง เพื่อค้นหาคำตอบ เราใช้การวิเคราะห์ผลกระทบ

การวิเคราะห์ผลกระทบ

ตามคำขอดึง เราจะรวบรวม git diff และค้นหาโมดูล Gradle ที่ถูกแก้ไข

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

ควรทำการทดสอบเครื่องมือวัดเพื่อตรวจสอบโมดูลที่เปลี่ยนแปลงและโมดูลทั้งหมดที่ขึ้นอยู่กับโมดูลเหล่านั้นเท่านั้น ไม่มีประโยชน์ในการรันการทดสอบสำหรับโมดูลข้างเคียง: โค้ดไม่มีการเปลี่ยนแปลงและไม่มีอะไรเสียหาย

การทดสอบเครื่องมือวัดนั้นไม่ง่ายนัก เนื่องจากจะต้องอยู่ในโมดูลแอปพลิเคชันระดับบนสุด เราใช้การวิเคราะห์พฤติกรรมร่วมกับการวิเคราะห์ bytecode เพื่อทำความเข้าใจว่าการทดสอบแต่ละครั้งเป็นของโมดูลใด

การอัปเกรดการดำเนินการของการทดสอบเครื่องมือวัดเพื่อให้ทดสอบเฉพาะโมดูลที่เกี่ยวข้องนั้นใช้เวลาประมาณแปดสัปดาห์

มาตรการเร่งรัดการตรวจสอบได้ผลสำเร็จ จาก 45 นาที เราเพิ่มเป็นประมาณ 15 นาที เป็นเรื่องปกติอยู่แล้วที่จะต้องรอประมาณหนึ่งในสี่ของชั่วโมงเพื่อสร้างงาน

แต่ตอนนี้นักพัฒนาเริ่มบ่นว่าพวกเขาไม่เข้าใจว่ามีการเปิดตัวบิลด์ใด จะดูบันทึกได้ที่ไหน เหตุใดบิลด์จึงเป็นสีแดง การทดสอบใดล้มเหลว ฯลฯ

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

ปัญหาเกี่ยวกับข้อเสนอแนะทำให้การพัฒนาช้าลง ดังนั้นเราจึงพยายามให้ข้อมูลที่ชัดเจนและละเอียดเกี่ยวกับ PR แต่ละรายการและสร้างให้มากที่สุดเท่าที่จะเป็นไปได้ เราเริ่มต้นด้วยความคิดเห็นใน Bitbucket ต่อ PR โดยระบุว่าบิลด์ใดล้มเหลวและเพราะเหตุใด และเขียนข้อความที่กำหนดเป้าหมายใน Slack ในท้ายที่สุด เราได้สร้างแดชบอร์ด PR สำหรับเพจที่มีรายการบิวด์ทั้งหมดที่กำลังทำงานอยู่และสถานะ: เข้าคิว กำลังทำงาน ขัดข้อง หรือเสร็จสมบูรณ์ คุณสามารถคลิกที่บิลด์และไปที่บันทึกของมัน

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

ใช้เวลาหกสัปดาห์ในการรับฟังความคิดเห็นโดยละเอียด

แผน

เรามาดูประวัติศาสตร์ล่าสุดกันดีกว่า หลังจากแก้ไขปัญหาข้อเสนอแนะแล้ว เราก็ก้าวไปสู่ระดับใหม่ - เราตัดสินใจสร้างฟาร์มจำลองของเราเอง เมื่อมีการทดสอบและอีมูเลเตอร์จำนวนมาก การจัดการก็จะทำได้ยาก ด้วยเหตุนี้ โปรแกรมจำลองทั้งหมดของเราจึงย้ายไปยังคลัสเตอร์ k8s พร้อมการจัดการทรัพยากรที่ยืดหยุ่น

นอกจากนี้ยังมีแผนอื่นๆ

  • กลับผ้าสำลี (และการวิเคราะห์ทางสถิตอื่นๆ) เรากำลังดำเนินการไปในทิศทางนี้แล้ว
  • ดำเนินการทุกอย่างบนตัวบล็อก PR การทดสอบแบบ end-to-end ใน SDK ทุกเวอร์ชัน

ดังนั้นเราจึงได้ติดตามประวัติความเป็นมาของการพัฒนาการบูรณาการอย่างต่อเนื่องใน Avito ตอนนี้ฉันต้องการให้คำแนะนำจากมุมมองที่มีประสบการณ์

Советы

หากฉันสามารถให้คำแนะนำได้เพียงข้อเดียวก็จะเป็นดังนี้:

โปรดระวังเชลล์สคริปต์!

Bash เป็นเครื่องมือที่ยืดหยุ่นและทรงพลังมาก สะดวกและรวดเร็วในการเขียนสคริปต์ แต่คุณสามารถติดกับดักของมันได้ และน่าเสียดายที่เราตกหลุมพรางนั้น

ทุกอย่างเริ่มต้นด้วยสคริปต์ง่ายๆ ที่ทำงานบนเครื่องสร้างของเรา:

#!/usr/bin/env bash
./gradlew assembleDebug

แต่อย่างที่คุณทราบทุกอย่างพัฒนาและซับซ้อนมากขึ้นเมื่อเวลาผ่านไป - มาเรียกใช้สคริปต์หนึ่งจากอีกสคริปต์หนึ่งส่งพารามิเตอร์บางตัวไปที่นั่น - ในที่สุดเราต้องเขียนฟังก์ชันที่กำหนดระดับของการซ้อน bash ที่เราอยู่ตอนนี้ตามลำดับ เพื่อแทรกเครื่องหมายคำพูดที่จำเป็นเพื่อเริ่มต้นทั้งหมด

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

คุณสามารถจินตนาการถึงต้นทุนแรงงานในการพัฒนาสคริปต์ดังกล่าวได้ ฉันแนะนำให้คุณอย่าตกหลุมพรางนี้

มีอะไรมาทดแทนได้บ้าง?

  • ภาษาสคริปต์ใด ๆ เขียนถึง สคริปต์ Python หรือ Kotlin สะดวกกว่าเพราะเป็นการเขียนโปรแกรมไม่ใช่สคริปต์
  • หรืออธิบายตรรกะการสร้างทั้งหมดในแบบฟอร์ม งานไล่ระดับที่กำหนดเอง สำหรับโครงการของคุณ

เราตัดสินใจเลือกตัวเลือกที่สอง และตอนนี้เรากำลังลบสคริปต์ทุบตีทั้งหมดอย่างเป็นระบบและเขียนงาน gradle แบบกำหนดเองจำนวนมาก

เคล็ดลับ #2: จัดเก็บโครงสร้างพื้นฐานไว้ในโค้ด

สะดวกเมื่อการตั้งค่าการรวมแบบต่อเนื่องไม่ได้เก็บไว้ในอินเทอร์เฟซ UI ของ Jenkins หรือ TeamCity ฯลฯ แต่อยู่ในรูปแบบของไฟล์ข้อความโดยตรงในพื้นที่เก็บข้อมูลโปรเจ็กต์ สิ่งนี้ทำให้มีความเป็นเวอร์ชันได้ การย้อนกลับหรือสร้างโค้ดในสาขาอื่นไม่ใช่เรื่องยาก

สคริปต์สามารถจัดเก็บไว้ในโครงการได้ จะทำอย่างไรกับสิ่งแวดล้อม?

เคล็ดลับ #3: นักเทียบท่าสามารถช่วยรักษาสิ่งแวดล้อมได้

มันจะช่วยนักพัฒนา Android ได้อย่างแน่นอน น่าเสียดายที่ iOS ยังไม่มี

นี่คือตัวอย่างของไฟล์นักเทียบท่าธรรมดาที่มี jdk และ android-sdk:

FROM openjdk:8

ENV SDK_URL="https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip" 
    ANDROID_HOME="/usr/local/android-sdk" 
    ANDROID_VERSION=26 
    ANDROID_BUILD_TOOLS_VERSION=26.0.2

# Download Android SDK
RUN mkdir "$ANDROID_HOME" .android 
    && cd "$ANDROID_HOME" 
    && curl -o sdk.zip $SDK_URL 
    && unzip sdk.zip 
    && rm sdk.zip 
    && yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses

# Install Android Build Tool and Libraries
RUN $ANDROID_HOME/tools/bin/sdkmanager --update
RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" 
    "platforms;android-${ANDROID_VERSION}" 
    "platform-tools"

RUN mkdir /application
WORKDIR /application

เมื่อเขียนไฟล์ Docker นี้ (ฉันจะบอกความลับแก่คุณคุณไม่จำเป็นต้องเขียน แต่เพียงดึงมันสำเร็จรูปจาก GitHub) และประกอบอิมเมจคุณจะได้เครื่องเสมือนที่คุณสามารถสร้างแอปพลิเคชันได้ และรันการทดสอบ Junit

เหตุผลหลักสองประการที่ทำให้สิ่งนี้สมเหตุสมผลคือความสามารถในการขยายขนาดและการทำซ้ำได้ ด้วยการใช้นักเทียบท่า คุณสามารถเพิ่ม build agent จำนวนมากที่จะมีสภาพแวดล้อมเหมือนกับรุ่นก่อนหน้าได้อย่างรวดเร็ว ทำให้ชีวิตของวิศวกร CI ง่ายขึ้นมาก มันค่อนข้างง่ายที่จะผลักดัน android-sdk ลงในนักเทียบท่า แต่ด้วยโปรแกรมจำลองมันยากขึ้นอีกเล็กน้อย: คุณจะต้องทำงานหนักขึ้นอีกเล็กน้อย (หรือดาวน์โหลดอันที่เสร็จแล้วจาก GitHub อีกครั้ง)

เคล็ดลับที่ 4: อย่าลืมว่าการตรวจสอบไม่ได้ทำเพื่อการตรวจสอบ แต่เพื่อประชาชน

ความคิดเห็นที่รวดเร็วและที่สำคัญที่สุดคือข้อเสนอแนะที่เข้าใจได้นั้นสำคัญมากสำหรับนักพัฒนา: สิ่งใดพัง สิ่งใดที่การทดสอบล้มเหลว ฉันจะดูบันทึกการสร้างได้ที่ไหน

เคล็ดลับ #5: จริงจังเมื่อพัฒนาการรวมระบบอย่างต่อเนื่อง

เข้าใจอย่างชัดเจนว่าคุณต้องการป้องกันข้อผิดพลาดประเภทใด จำนวนทรัพยากร เวลา และเวลาคอมพิวเตอร์ที่คุณยินดีจ่าย เช็คที่ใช้เวลานานเกินไปสามารถถูกเลื่อนออกไปข้ามคืนได้ และข้อผิดพลาดที่ไม่สำคัญมากก็ควรละทิ้งไปโดยสิ้นเชิง

เคล็ดลับ #6: ใช้เครื่องมือสำเร็จรูป

ขณะนี้มีหลายบริษัทที่ให้บริการ cloud CI

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

นี่เป็นทางออกที่ดีสำหรับทีมขนาดเล็ก คุณไม่จำเป็นต้องสนับสนุนอะไรเลย เพียงแค่จ่ายเงินเพียงเล็กน้อย สร้างแอปพลิเคชันของคุณ และแม้แต่เรียกใช้การทดสอบเครื่องมือวัด

เคล็ดลับ #7: ในทีมขนาดใหญ่ โซลูชันภายในองค์กรจะให้ผลกำไรมากกว่า

แต่ไม่ช้าก็เร็ว เมื่อทีมเติบโตขึ้น โซลูชันภายในองค์กรจะทำกำไรได้มากขึ้น มีปัญหาประการหนึ่งกับการตัดสินใจเหล่านี้ มีกฎว่าด้วยผลตอบแทนทางเศรษฐกิจที่ลดลง ในโครงการใดๆ การปรับปรุงแต่ละครั้งจะยากขึ้นเรื่อยๆ และต้องใช้เงินลงทุนมากขึ้นเรื่อยๆ

เศรษฐศาสตร์อธิบายถึงชีวิตทั้งชีวิตของเรา รวมถึงการบูรณาการอย่างต่อเนื่อง ฉันสร้างตารางค่าแรงสำหรับการพัฒนาแต่ละขั้นตอนของการบูรณาการอย่างต่อเนื่องของเรา

วิวัฒนาการของ CI ในทีมพัฒนามือถือ

เป็นที่ชัดเจนว่าการปรับปรุงใดๆ ก็ตามนั้นยากขึ้นเรื่อยๆ เมื่อดูกราฟนี้ คุณจะเข้าใจได้ว่าการบูรณาการอย่างต่อเนื่องจำเป็นต้องได้รับการพัฒนาให้สอดคล้องกับการเติบโตของขนาดทีม สำหรับทีมที่มีสมาชิกสองคน การใช้เวลา 50 วันในการพัฒนาฟาร์มจำลองภายในถือเป็นแนวคิดที่ธรรมดามาก แต่ในขณะเดียวกัน สำหรับทีมใหญ่ การไม่ทำ Continuous Integration เลยก็เป็นความคิดที่ไม่ดีเช่นกัน เพราะมีปัญหาในการบูรณาการ แก้ไขปัญหาการสื่อสาร เป็นต้น มันจะต้องใช้เวลามากขึ้น

เราเริ่มต้นด้วยแนวคิดที่ว่าระบบอัตโนมัติเป็นสิ่งจำเป็นเนื่องจากผู้คนมีราคาแพง พวกเขาทำผิดพลาด และเกียจคร้าน แต่ผู้คนก็ทำโดยอัตโนมัติเช่นกัน ดังนั้นปัญหาเดียวกันทั้งหมดจึงมีผลกับระบบอัตโนมัติ

  • ระบบอัตโนมัติมีราคาแพง จำตารางแรงงาน
  • เมื่อพูดถึงระบบอัตโนมัติ ผู้คนมักทำผิดพลาด
  • บางครั้งมันก็ขี้เกียจมากที่จะทำให้เป็นอัตโนมัติ เพราะทุกอย่างเป็นแบบนั้น เหตุใดจึงต้องปรับปรุงสิ่งอื่นใด เหตุใดจึงต้องบูรณาการอย่างต่อเนื่องทั้งหมดนี้

แต่ฉันมีสถิติ: ข้อผิดพลาดเกิดขึ้นได้ใน 20% ของแอสเซมบลี และนี่ไม่ใช่เพราะนักพัฒนาของเราเขียนโค้ดได้ไม่ดี เนื่องจากนักพัฒนามั่นใจว่าหากพวกเขาทำผิดพลาด จะไม่จบลงด้วยการพัฒนา แต่จะถูกตรวจสอบโดยอัตโนมัติ ด้วยเหตุนี้ นักพัฒนาจึงสามารถใช้เวลาในการเขียนโค้ดและสิ่งที่น่าสนใจได้มากขึ้น แทนที่จะใช้งานและทดสอบบางอย่างในเครื่อง

ฝึกบูรณาการอย่างต่อเนื่อง แต่ในปริมาณที่พอเหมาะ

อย่างไรก็ตาม Nikolai Nesterov ไม่เพียงแต่ให้รายงานที่ยอดเยี่ยมเท่านั้น แต่ยังเป็นสมาชิกของคณะกรรมการโครงการอีกด้วย AppsConf และช่วยผู้อื่นเตรียมสุนทรพจน์ที่มีความหมายสำหรับคุณ ความสมบูรณ์และประโยชน์ของโปรแกรมการประชุมครั้งต่อไปสามารถประเมินได้ตามหัวข้อใน กำหนดการ. และดูรายละเอียดเพิ่มเติมได้ที่ Infospace วันที่ 22-23 เมษายน

ที่มา: will.com

เพิ่มความคิดเห็น