Теорія
GitHub flow: малі гілки, швидкі мерджі
Цикл, у якому працює більшість продуктових команд: гілка від main, pull request, рев'ю й перевірки, мердж, деплой. Чому малі короткоживучі гілки безпечніші, що означають «змерджено» і «викладено», як фіча-флаги дають викласти незавершену роботу прихованою і як читати прогрес фічі зі списку pull requests.
~10 хв читання · 11 вправ · 250 балів
Перед початком
Це вже на сайті?
П'ятниця, друга половина дня в Котокормі, одразу після зустрічі продуктової команди. Сьогодні одне питання прозвучало тричі: «Це вже на сайті?» Раз — про виправлення кнопки оформлення, двічі — про банер осіннього розпродажу від Emma.
Цей модуль — про те, як команди випускають зміни. У першому уроці ти покладеш на одну картинку весь шлях зміни від гілки до живого сайту й побачиш, чому малі короткоживучі гілки безпечніші і як незавершену роботу можна випустити прихованою.
GitHub flow: цикл, у якому працює більшість продуктових команд
GitHub flow це короткий цикл навколо одного правила: main завжди версія, яку можна хоч зараз викласти на сайт. Кожна зміна проходить той самий шлях:
- Створити короткоживучу гілку зі свіжої
main:git switch -c fix/footer-links main. - Закомітити зміну й зробити пуш:
git push -u origin fix/footer-links. - Відкрити pull request у
main(gh pr create --base main). - Рев'юери схвалюють, обов'язкові перевірки проходять.
- Мердж, у Котокорму через
Squash and merge(gh pr merge --squash --delete-branch). - Деплой викладає нову
mainна живий сайт. - Перевірити зміну на живому сайті власними очима.

Після мерджу гілку видаляють, і наступна зміна знову починається з оновленої main. Усе незавершене чекає у своїй гілці або, як буде видно далі, за перемикачем.
У Котокорму тексти від Emma, поріг безкоштовної доставки від Daniel і виправлення кнопки пройшли цей шлях за тиждень або швидше. Три короткі гілки, три squash-коміти в main, і кожен на сайті за кілька хвилин після мерджу.
e7c3b19Add summer promo bannere19c3b7Rewrite delivery page copyf02a6d4Update FAQ answers3a9d1c4Reproduce bug in a test8be2f70Add cat treats bundle5c1d7e2Set freeDeliveryFrom to 1000a8f40b9Update delivery page textd36b9c5Update cart hint textfeature/free-delivery-1000b7e4a05Re-enable button after cart edits6f2c8a1Raise free-delivery threshold to 1000 UAH (#133)c90d4e7Merge branch 'main' into content/delivery-copycontent/delivery-copya7d93e4Rewrite delivery page copy (#132)c2d7f90Update checkout hint copyf7a2b61Restore checkout button colour9e3b5d2Merge branch 'main' into fix/checkout-buttonfix/checkout-buttond51c7e8Fix checkout button after cart edits (#134)main
Граф комітів, від найстарішого: main за останні два тижні 1. коміт e7c3b19 у main "Add summer promo banner" 2. гілка content/delivery-copy починається від коміту e7c3b19 3. коміт e19c3b7 у content/delivery-copy "Rewrite delivery page copy" 4. коміт f02a6d4 у main "Update FAQ answers" 5. гілка fix/checkout-button починається від коміту f02a6d4 6. коміт 3a9d1c4 у fix/checkout-button "Reproduce bug in a test" 7. коміт 8be2f70 у main "Add cat treats bundle" 8. гілка feature/free-delivery-1000 починається від коміту 8be2f70 9. коміт 5c1d7e2 у feature/free-delivery-1000 "Set freeDeliveryFrom to 1000" 10. коміт a8f40b9 у feature/free-delivery-1000 "Update delivery page text" 11. коміт d36b9c5 у feature/free-delivery-1000 "Update cart hint text" 12. коміт b7e4a05 у fix/checkout-button "Re-enable button after cart edits" 13. коміт 6f2c8a1: feature/free-delivery-1000 змерджено в main через squash "Raise free-delivery threshold to 1000 UAH (#133)" 14. мердж-коміт c90d4e7: main змерджено в content/delivery-copy "Merge branch 'main' into content/delivery-copy" 15. коміт a7d93e4: content/delivery-copy змерджено в main через squash "Rewrite delivery page copy (#132)" 16. коміт c2d7f90 у fix/checkout-button "Update checkout hint copy" 17. коміт f7a2b61 у fix/checkout-button "Restore checkout button colour" 18. мердж-коміт 9e3b5d2: main змерджено в fix/checkout-button "Merge branch 'main' into fix/checkout-button" 19. коміт d51c7e8: fix/checkout-button змерджено в main через squash "Fix checkout button after cart edits (#134)" (виділено)
- Так
git switch main,git pull, потімgit switch -c fix/footer-linksКожна зміна починається зі свіжої
main. - Не такДалі працювати в гілці минулого місяця, «ще одна фіча, і все».
Гілка відстає від
mainі стає непридатною для рев'ю.
Змерджено, потім викладено: дві різні події
«Змерджено» і «викладено» це дві події. Мердж змінює main у репозиторії. Деплой (deploy) бере цю main, збирає сайт і викладає його на сервери, якими користуються клієнти. У Котокорму деплой це процес GitHub Actions, завдання на кшталт build і test, тільки воно запускається на main після кожного мерджу. Між ними кілька хвилин, і друге йде за першим саме. В інших командах між ними можуть бути дні чи тижні.
Тож одразу після мерджу клієнти ще бачать старий сайт, а новий побачать, лише якщо деплой вдасться.
Перевірити можна, нікого не питаючи. Станом на 2026 рік на сторінці репозиторію в правій колонці є розділ Deployments; натисни production, щоб побачити, який коміт зараз на сайті і відколи. У терміналі gh run list --branch main показує останні запуски процесів на main, зокрема деплої.
Fix checkout button after cart edits#134
Змерджено в main о 9:40 як d51c7e8
deploy (production)
Запускався на d51c7e8 у main, завершився о 9:44
Перш ніж казати підтримці, що щось уже на сайті, зроби обидві речі: перевір production у Deployments, а тоді глянь на живий сайт. Деплой каже, що код прибув; власні очі кажуть, що він працює.
- Так«
productionпоказуєd51c7e8з 9:46, і кнопка працює на моєму телефоні. Уже на сайті».Перевірено і деплой, і живий сайт.
- Не так«#134 каже Merged, отже, клієнти вже мають виправлення».
Деплой може ще тривати або взагалі впасти.
Коли деплой падає
Буває, що мердж проходить, а деплой ні. Тоді зміна є в main, але не на сайті: клієнти й далі бачать попередню версію, ту, яку виклав останній успішний деплой. Нічого не встановлено наполовину; нова версія просто не прибула.
Команда отримує сповіщення, і має два виходи:
- Виправити вперед: маленький pull request, який лагодить те, що зламало деплой, і швидкий мердж.
- Відкотити (revert): скасувати мердж. Кнопка
Revertна змердженому pull request відкриває новий pull request, який прибирає саме цю зміну; у терміналі те саме робитьgit revertіз хешем коміту.
У будь-якому разі це йде поза чергою. У GitHub flow main має лишатися придатною до деплою, а зламана main блокує наступний деплой для всіх, а не лише для однієї зміни.
Для PM упалий деплой ще одна причина розрізняти «змерджено» і «на сайті». Якщо колега питає про змерджену зміну, а останній запуск у Deployments упав, чесна відповідь: «змерджено, на сайті ще немає, команда вже розбирається».
- Так«#160 змерджено, але деплой упав. Клієнти досі бачать стару сторінку; Daniel уже виправляє».
Відділяє «змерджено» від «на сайті».
- Не так«Деплой упав, отже, сайт лежить».
Після невдалого деплою працює попередня версія.
Малі й короткоживучі: менше ризику на мердж
Зібрати всю фічу в одній гілці й змерджити її раз наприкінці звучить охайно. На практиці це закінчується як feature/loyalty-points у Котокорму: дванадцять комітів попереду, 86 позаду, п'ять місяців ніхто не чіпав.
Малі pull requests у короткоживучих гілках безпечніші з трьох причин:
- Менше відставання. Кожен день життя гілки
mainіде вперед без неї. Що більше відставання, то більше конфліктів і несподіванок, коли стара робота зустрічає новий код. - Краще рев'ю. Сорок змінених рядків це десять уважних хвилин. Дві тисячі чекають на чийсь вільний вечір, а тоді їх проглядають навскоси.
- Точний відкат. Якщо одна мала зміна щось зламала, revert прибирає саме її. Відкотиш величезну, і разом із поганою зміною підуть десять добрих.

Тож малість не про охайність, а про менший ризик на кожен мердж. Просте правило багатьох команд: гілка живе кілька днів, а pull request можна переглянути за один підхід. GitHub не обмежує розмір і не дає малим pull requests пільг у правилах рев'ю; користь лише в тому, наскільки безпечно вони виходять.
- ТакТри pull requests приблизно по 50 рядків, змерджені за тиждень.
Кожен уважно переглянуто, і кожен можна відкотити окремо.
- Не такОдин pull request на 2 000 змінених рядків після місяця в гілці.
Поверхове рев'ю, болючі конфлікти, відкат «усе або нічого».
Як рано помітити ризиковану гілку
Гілку, що прямує до проблем, можна помітити ще до рев'ю. Подивися на два числа:
- Коли її востаннє оновлювали. Гілка, якої тижнями ніхто не чіпав, або завершена й забута, або застрягла.
- Наскільки вона відстає від
main. Сторінка гілок показує для кожної лічильники «behind» (позаду) і «ahead» (попереду). У терміналіgit rev-list --left-right --count main...feature/wishlistдрукує обидва: перше число це скільки комітів є вmain, але немає в гілці (відставання), друге скільки є в гілці, але немає вmain(випередження).
Стара й далеко позаду: ось тривожний знак. На графі комітів це довга смуга, що почалася високо вгорі, поки main поруч збирала коміти. Гілка, що почалася з найсвіжішої main і містить один малий коміт, протилежність: мало що переглядати й нічого не треба наздоганяти.
Pull request розповідає те саме. Якщо Files changed показує 60 файлів для «дрібної правки», спитай чому. Дружнє питання на початку дешевше за болюче рев'ю чи ризикований мердж згодом.
- Так«
feature/wishlistвідстає на 5 комітів і містить п'ять комітів роботи. Може, розділимо її до рев'ю?»Читає відставання й розмір, питає рано.
- Не так«У неї більше комітів, отже, вона краще протестована».
Більше комітів у старій гілці означає більше ризику, а не більше тестів.
Розріж велику фічу на малі pull requests
Фіча, завелика для одного рев'ю, не потребує однієї великої гілки. Розріж її на серію малих pull requests, кожен з яких змерджують і викладають окремо, а незавершені частини ховаються за одним фіча-флагом.
Для балів лояльності в Котокорму план нарізки може виглядати так:
- Нараховувати бали за оплачені замовлення, зберігати, але ніде не показувати.
- Показувати баланс в акаунті клієнта, за флагом
loyalty-points. - Списувати бали як знижку під час оформлення, за тим самим флагом.
- Сторінка від Emma з поясненням програми, теж за флагом.
Кожен шматок переглядають за один підхід, і він виходить на сайт непомітно. Спершу команда вмикає флаг лише для працівників і пробує все на справжньому сайті. Фічу запущено лише того дня, коли флаг вмикають для всіх клієнтів, а відкат означає його вимкнути.
Альтернатива, перебудувати все в одній довгій гілці й змерджити наприкінці, повертає всі проблеми feature/loyalty-points: відставання, рев'ю, яке неможливо закінчити, і запуск «усе або нічого».
- ТакЧотири малі pull requests за
loyalty-points: спершу лише для працівників, потім для всіх.Кожен шматок виходить безпечно, а запуск це перемикач.
- Не такПеребудувати бали лояльності в одній гілці й змерджити за тиждень до Black Friday.
Одне величезне рев'ю й один ризикований день запуску.
Фіча-флаги: змерджено, викладено, приховано
Фіча-флаг (feature flag) це перемикач навколо частини сайту. Код є в main і викладений, але клієнти бачать цю частину, лише поки флаг увімкнено.
Це розв'язує справжню проблему з часом. Осінній розпродаж у Котокорму починається в понеділок. Змерджиш банер від контент-маркетологині Emma в п'ятницю без флагу, і він оголосить розпродаж, який ще не почався. Чекатимеш до ранку понеділка, і рев'ю, мердж і деплой стануться поспіхом, коли клієнти вже заходять.
З флагом Daniel загорнув банер у перевірку autumn-sale-banner, вимкнену за замовчуванням. Pull request #136 від Emma переглянули й змерджили в п'ятницю, і деплой виклав код на сайт. У суботу клієнт, що відкриває головну, бачить те саме, що й раніше: код на сайті, флаг вимкнено. Жодних помилок і жодного мерехтіння.

Тож із флагами в однієї зміни три стани: змерджено (у main), викладено (на серверах) і видно (увімкнено для клієнтів). Змерджений pull request із вимкненим флагом означає «готово й приховано», а не «запущено».
- Так«#136 змерджено й викладено, він схований за
autumn-sale-bannerдо понеділка, 8:00».Усі три стани в одному реченні.
- Не так«#136 змерджено, отже, клієнти бачать банер».
Що бачать клієнти, вирішує флаг.
Перемикання флагу: без деплою, а потім прибирання
У Котокорму перемикачі флагів живуть у панелі налаштувань флагів поза репозиторієм. Щоб увімкнути чи вимкнути флаг, не потрібні ні pull request, ні рев'ю, ні деплой, і діє це за лічені секунди.
Це змінює сам запуск. У понеділок о 8:00 Emma вмикає autumn-sale-banner, і клієнти бачать банер майже одразу. Якщо щось не так, вона вимикає його, теж без деплою. Вимкнути флаг набагато швидше, ніж відкотити мердж.

Флаги ще й дозволяють зливати незавершену роботу: недобудована частина лишається вимкненою, тож main залишається придатною до деплою, поки фіча росте малими pull requests. Багато систем флагів уміють вмикати флаг лише для частини людей, наприклад для працівників, раніше, ніж для всіх.
Підступ: кожен флаг це зайве «якщо» в коді. Коли фічу ввімкнено назавжди, хтось має прибрати флаг малим pull request, інакше код заповниться забутими перемикачами, яких ніхто не наважується торкатися. Додай «прибрати флаг» у план із першого дня.
- ТакПонеділок, 8:00: увімкнути флаг, перевірити головну, вимкнути, якщо щось не так.
Запуск і відкат за секунди.
- Не такЛишити
autumn-sale-bannerу коді на рік після кінця розпродажу.Забуті флаги накопичуються мертвими «якщо».
Як читати прогрес фічі
Коли фічу будують малими pull requests за флагом, питання «де вона?» має дві половини, і PM читає обидві.
Список pull requests показує, наскільки просунулося будівництво. Познач кожен шматок однією міткою й відфільтруй за нею: loyalty у списку або gh pr list --label loyalty --state all у терміналі. Стан кожного рядка каже етап:
Draft: ще пишеться; змерджити поки не можна.- Відкритий, «Review required», перевірки пройшли: зібрано й перевірено роботами, чекає на схвалення людини.
Merged: уmainі викладено, але з вимкненим флагом клієнти цього не бачать.
Add loyalty points behind a flag#155
автор Daniel · змерджено у вівторок · флаг loyalty-points: вимкнено
Earn points on paid orders#156
автор Daniel · Review required · перевірки пройдено
Write loyalty programme page#157
авторка Emma · Draft
Флаг показує, що бачать клієнти. Вимкнено: не бачить ніхто. Увімкнено для працівників: команда тестує на справжньому сайті. Увімкнено для всіх: це і є запуск.
Тож запуск це мить, коли флаг вмикають для всіх, а не мить, коли змерджено останній pull request. Тому ніхто не має анонсувати бали лояльності лише через те, що код лояльності на сайті з вівторка.
- Так«#155 змерджено й приховано, #156 чекає на рев'ю, #157 чернетка. Флаг вимкнено: не запущено».
Прогрес будівництва плюс те, що бачать клієнти.
- Не так«Код лояльності на сайті з вівторка, анонсуймо».
Код на сайті за вимкненим флагом досі прихований.
Готові потренуватися?
Виконайте вправи уроку й отримайте 250 балів, коли пройдете його вперше.
Розпочати вправи