Питання на співбесіді з Git
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
100 питань
Хуки - виконувані скрипти, які Git запускає в певні моменти: перед комітом, після checkout, перед push. Лежать у .git/hooks/ (Git створює там приклади з суфіксом .sample) і мають бути виконуваними (chmod +x).
Клієнтські хуки, які використовують найчастіше:
| Хук | Коли | Типове використання |
|---|---|---|
pre-commit |
перед створенням коміту | форматування, лінтер, швидкі перевірки |
prepare-commit-msg |
перед відкриттям редактора повідомлення | підставити номер задачі з назви гілки |
commit-msg |
після введення повідомлення | перевірити формат (Conventional Commits) |
post-checkout |
після checkout/switch |
оновити залежності, очистити кеш |
pre-push |
перед відправкою на сервер | запустити тести |
Ненульовий код виходу скасовує операцію - так pre-commit може заборонити коміт:
#!/bin/sh
# .git/hooks/pre-commit
vendor/bin/pint --test || {
echo "Код не відформатовано. Запустіть vendor/bin/pint"
exit 1
}
Що отримують хуки:
commit-msg- шлях до файла з повідомленням коміту (першим аргументом);pre-push- назву й адресу віддаленого репозиторію в аргументах, а в стандартному вводі - рядки з посиланнями й SHA, які відправляються.
Важливі обмеження:
.git/hooksне потрапляє в репозиторій - хуки не поширюються командою автоматично (для цього -core.hooksPathчи інструменти на кшталт Husky і CaptainHook);- хуки легко обійти:
git commit --no-verify(чи-n) пропускаєpre-commitіcommit-msg,git push --no-verify-pre-push. Тому хуки - зручність для розробника, а гарантію дає CI; - хуки мають бути швидкими:
pre-commitна хвилину - і люди почнуть використовувати--no-verifyпостійно. Повний набір тестів - уpre-pushчи CI.
Каталог .git/ не версіонується, тож хуки, покладені в .git/hooks, живуть лише на вашій машині. Є кілька способів зробити їх спільними.
1. core.hooksPath - вбудований механізм Git:
mkdir .githooks
# покласти туди pre-commit, commit-msg... і зробити виконуваними
chmod +x .githooks/*
git config core.hooksPath .githooks
Хуки лежать у репозиторії й проходять рев'ю, але кожен розробник має один раз виконати git config. Його зручно додати в скрипт налаштування проєкту:
{
"scripts": {
"post-install-cmd": ["git config core.hooksPath .githooks || true"]
}
}
2. CaptainHook - для PHP-проєктів: конфігурація в captainhook.json, хуки встановлюються командою vendor/bin/captainhook install (або автоматично через Composer-плагін):
{
"pre-commit": {
"enabled": true,
"actions": [
{ "action": "vendor/bin/pint --test" },
{ "action": "vendor/bin/phpstan analyse --no-progress" }
]
}
}
3. GrumPHP - інший PHP-інструмент: набір готових задач (phpcs, phpstan, phpunit, перевірка повідомлень комітів), конфігурація в grumphp.yml, хуки ставить сам.
4. Husky - якщо в проєкті є Node: npx husky init створює каталог .husky/ і через скрипт prepare у package.json налаштовує core.hooksPath при кожному npm install. Часто разом з lint-staged, що запускає інструменти лише на змінених файлах.
Як обрати: PHP-бекенд без Node - CaptainHook чи GrumPHP; є фронтенд і npm install - Husky; мінімум залежностей - core.hooksPath.
Пам'ятайте: хуки - зручність, а не гарантія. Будь-хто може їх не встановити чи пропустити через --no-verify, тому ті самі перевірки обов'язково дублюються в CI.
Laravel Pint - форматувальник коду на основі PHP CS Fixer. Корисні режими:
vendor/bin/pint # виправити всі файли
vendor/bin/pint --test # лише перевірити, ненульовий код при помилках
vendor/bin/pint --dirty # лише файли з незакоміченими змінами
vendor/bin/pint --diff=main # лише файли, що відрізняються від гілки main
vendor/bin/pint --repair # виправити, але повернути ненульовий код, якщо було що виправляти
Простий pre-commit хук:
#!/bin/sh
files=$(git diff --cached --name-only --diff-filter=ACMR -- '*.php')
[ -z "$files" ] && exit 0
vendor/bin/pint $files
git add $files
git diff --cached- лише файли, додані в індекс (тобто ті, що потраплять у коміт);--diff-filter=ACMR- додані, скопійовані, змінені й перейменовані; видалені файли форматувати нема чого;git addпісля форматування - щоб виправлення потрапили в коміт.
Пастка частково доданих файлів: якщо у файлі частина змін додана в індекс (git add -p), а частина ні, то git add $files після Pint додасть у коміт і ті зміни, які ви навмисно не додавали. Варіанти:
- лише перевіряти, а не виправляти:
vendor/bin/pint --test $files- коміт блокується, розробник сам запускає Pint; - використати інструмент, що вміє працювати з частковим індексом (lint-staged ховає неіндексовані зміни на час перевірки).
--dirty у хуку - зручно, але він бере файли з будь-якими незакоміченими змінами, а не лише з індексу.
Що варто знати:
- форматування в хуку - зручність; в CI має бути
pint --test, щоб неформатований код не потрапив уmainчерез--no-verify; - конфігурація в
pint.json- однакова для хуку, CI й редактора; - великий одноразовий перехід на Pint краще зробити окремим комітом і додати його SHA у
.git-blame-ignore-revs, щобgit blameне показував форматування як автора всіх рядків.
GitHub Actions - вбудований у GitHub CI/CD. Робочі процеси (workflows) описуються в YAML у .github/workflows/ і запускаються на події: push, pull request, розклад, ручний запуск.
Основні поняття:
- workflow - файл з описом процесу;
- event (
on) - що запускає процес; - job - набір кроків, що виконується на окремій віртуальній машині (runner); jobs ідуть паралельно, якщо не вказано залежність
needs; - step - команда (
run) чи готова дія (uses) з Marketplace.
Мінімальний workflow для Laravel:
# .github/workflows/tests.yml
name: tests
on:
pull_request:
push:
branches: [main]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
coverage: none
- run: composer install --no-interaction --prefer-dist
- run: cp .env.example .env && php artisan key:generate
- run: vendor/bin/pint --test
- run: php artisan test --compact
Що відбувається: на кожен PR і push у main GitHub піднімає чисту машину з Ubuntu, завантажує код, встановлює PHP і залежності, перевіряє стиль і запускає тести. Результат видно в PR як перевірку - зелену чи червону.
Чому це важливо:
- те, що «працює в мене», перевіряється в чистому середовищі;
- хуки можна пропустити, а CI - ні, особливо з обов'язковими перевірками в захисті гілки;
- рецензент не витрачає час на код, який не проходить тести.
Корисно знати:
- для публічних репозиторіїв стандартні runner-и безкоштовні, для приватних є місячна квота хвилин;
- секрети (ключі API) - у Settings → Secrets і доступні як
${{ secrets.NAME }}; у PR з форків секрети за замовчуванням не передаються; shivammathur/setup-php- де-факто стандарт для PHP: версії, розширення, інструменти.
Dependabot - бот GitHub, що стежить за залежностями й сам відкриває pull request-и з оновленнями. Розробнику лишається переглянути зміни, дочекатися зеленого CI і злити.
Два режими:
- security updates - PR лише для залежностей з відомими вразливостями (на основі GitHub Advisory Database);
- version updates - регулярні PR з новими версіями за розкладом.
Конфігурація для Laravel-проєкту:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: composer
directory: /
schedule:
interval: weekly
groups:
laravel:
patterns: ["laravel/*"]
open-pull-requests-limit: 5
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
Що тут налаштовано:
- екосистеми: Composer, npm і навіть версії дій у workflow GitHub Actions;
groups- кілька оновлень в одному PR (усі пакетиlaravel/*разом), щоб не тонути в десятках дрібних PR;open-pull-requests-limit- скільки PR бот тримає відкритими одночасно;schedule- як часто перевіряти.
Чому це варто робити:
- маленькі регулярні оновлення простіші за одне велике раз на рік, коли накопичилися мажорні версії й breaking changes;
- вразливості закриваються швидко, а не після інциденту;
- оновлення проходять той самий процес, що й код: CI, рев'ю, історія.
Без доброго набору тестів Dependabot корисний наполовину: зелений CI має реально означати «оновлення нічого не зламало». Тому тести - передумова для сміливих оновлень.
Корисні опції: ignore - пропускати певні пакети чи мажорні версії, cooldown - не пропонувати версію одразу після публікації, а почекати кілька днів (захист від зламаних чи скомпрометованих релізів).
Докладніше в документації: GitHub: Dependabot version updates
git rebase -i відкриває список комітів гілки, де кожному можна задати дію. Так історію з «wip», «fix typo», «ще фікс» перетворюють на кілька змістовних комітів.
git rebase -i main
pick a1b2c3d Add invoice model
squash d4e5f6a fix typo
pick 9f8e7d6 Send invoice email
fixup 1a2b3c4 forgot import
reword 5d6e7f8 wip
Основні дії:
pick- лишити коміт як є.reword- змінити повідомлення.squash- злити з попереднім, об'єднавши повідомлення.fixup- злити з попереднім, відкинувши повідомлення.edit- зупинитися на коміті, щоб змінити його чи розбити на кілька.dropабо видалити рядок - прибрати коміт.- Переставити рядки - змінити порядок комітів.
Зручний прийом - fixup-коміти:
git commit --fixup=a1b2c3d # «виправлення до коміту a1b2c3d»
git rebase -i --autosquash main # сам поставить fixup у потрібне місце
Навіщо: рев'юеру легше читати кілька логічних комітів, ніж двадцять випадкових; git bisect і git blame працюють точніше; коміт можна відкотити цілком.
Обережно: це переписування історії. Лише для своєї гілки, а потім - git push --force-with-lease. Якщо щось пішло не так, git rebase --abort повертає все як було, а після завершення врятує git reflog.
Багато команд обходяться без цього, вливаючи PR через squash merge - тоді вся гілка стає одним комітом у main.
git bisect шукає коміт, що вніс помилку, двійковим пошуком. Ви називаєте один «поганий» коміт (де баг є) і один «хороший» (де його ще не було), а Git щоразу переходить на середину проміжку й питає, чи є там баг.
git bisect start
git bisect bad # поточний коміт - зламаний
git bisect good v2.3.0 # у цьому релізі все працювало
# Git перемикається на коміт посередині
# перевіряєте й кажете:
git bisect good # або
git bisect bad
# ...через кілька кроків:
# a1b2c3d is the first bad commit
git bisect reset # повернутися, звідки почали
Серед 1000 комітів винуватця знайдено за ~10 перевірок.
Автоматично - якщо є команда, що відповідає «добре/погано» кодом виходу:
git bisect run php artisan test --filter=InvoiceTotalTest
Код 0 - коміт хороший, 1-127 (крім 125) - поганий, 125 - пропустити (наприклад, коміт не збирається).
Що допомагає bisect:
- Невеликі коміти, кожен з яких працює. Якщо коміт «напівготовий» і падає з іншої причини, bisect заплутається - такі коміти пропускають (
git bisect skip). - Тест, що відтворює баг, - його пишуть першим, а потім ганяють через
bisect run.
Знайшовши коміт, видно не лише рядок, а й контекст: повідомлення, задачу, автора - часто це пояснює, чому баг з'явився.
Звичайний git rebase main переносить усі коміти поточної гілки, яких немає в main. Але інколи потрібно перенести лише частину комітів чи змінити основу на зовсім іншу гілку. Для цього є --onto:
git rebase --onto <нова-основа> <стара-основа> <гілка>
Git бере коміти з діапазону стара-основа..гілка і відтворює їх поверх нова-основа.
Сценарій 1 - гілку створено не від тієї гілки. Гілку feature почали від develop, а треба було від main:
main ── A
\
develop B ── C
\
feature D ── E
git rebase --onto main develop feature
main ── A ── D' ── E' (feature)
Коміти B і C з develop у feature не потрапили.
Сценарій 2 - залежні pull request. Гілка part-2 побудована поверх part-1. Після злиття part-1 через squash її коміти в main мають інші хеші, і звичайний git rebase main намагатиметься застосувати їх повторно з конфліктами:
git rebase --onto main part-1 part-2
Переноситься лише власна робота part-2.
Сценарій 3 - видалити коміти з середини гілки:
git rebase --onto HEAD~5 HEAD~3
Коміти HEAD~4 і HEAD~3 зникають, решта переноситься на HEAD~5.
Що пам'ятати:
- як і будь-який rebase,
--ontoстворює нові коміти з новими хешами - для вже опублікованої спільної гілки це переписування історії; - якщо заплуталися -
git rebase --abortпід час процесу чиgit reflogпісля нього; --update-refs(Git 2.38+) оновлює й проміжні гілки в ланцюжку - зручно для стопки залежних гілок.
Докладніше в документації: git rebase: перенесення гілки з --onto
Злити гілку feature у main можна трьома способами, і від вибору залежить вигляд історії.
Fast-forward - можливий, коли main не просунувся після створення feature. Git просто пересуває вказівник main на останній коміт feature, нового коміту не створюється:
до: main ── A після: A ── B ── C (main, feature)
\
B ── C (feature)
Історія лінійна, але факт існування гілки зникає: не видно, які коміти були однією задачею.
--no-ff - завжди створює коміт злиття, навіть коли fast-forward можливий:
git merge --no-ff feature
A ────────── M (main)
\ /
B ── C ── (feature)
Видно межі задачі; відкотити всю функціональність можна одним git revert -m 1 M. Ціна - більше комітів злиття в історії.
Squash - усі зміни гілки стають одним новим комітом в main:
git merge --squash feature
git commit -m "Add invoice export"
Історія main чиста: одна задача - один коміт. Але:
- проміжні коміти гілки в
mainне потрапляють - деталізація дляgit bisectіblameвтрачається; - Git не вважає
featureзлитою (git branch --mergedїї не покаже), аgit branch -d featureвідмовиться її видаляти; - якщо продовжити роботу в тій самій гілці, наступний squash може дати конфлікти з уже злитими змінами.
Порівняння:
| Fast-forward | --no-ff |
Squash | |
|---|---|---|---|
| коміт злиття | ні | так | ні |
проміжні коміти в main |
так | так | ні |
| межі задачі видно | ні | так | так (один коміт) |
Налаштування: git config merge.ff only дозволяє лише fast-forward (злиття впаде, якщо воно неможливе), pull.ff only - те саме для git pull. На GitHub і GitLab спосіб злиття pull request обирається в налаштуваннях репозиторію.
git blame показує для кожного рядка файлу, в якому коміті й ким він востаннє змінений:
git blame app/Services/Billing.php
git blame -L 40,60 app/Services/Billing.php # лише рядки 40-60
a1b2c3d4 (Olena 2026-03-14 41) $total = round($subtotal * 1.2, 2);
Мета - не «кого звинуватити», а «чому так»: знайдений хеш веде до коміту з повідомленням, pull request і обговоренням, де видно причину рішення.
git show a1b2c3d4
Проблема: шумні коміти. Масове форматування (Pint, Prettier), перейменування чи перенесення коду роблять автором кожного рядка «коміт форматування», і справжня історія ховається.
Що допомагає:
| Параметр | Що робить |
|---|---|
-w |
ігнорує зміни пробілів |
-M |
розпізнає рядки, переміщені в межах файлу |
-C |
розпізнає рядки, перенесені з інших файлів того самого коміту (-C -C -C - з будь-якого коміту) |
--ignore-rev <хеш> |
пропустити конкретний коміт і показати попереднього автора |
Файл ігнорованих комітів - рішення для всієї команди:
# .git-blame-ignore-revs
# Pint: форматування всього проєкту
3f9e1a2b7c...
git config blame.ignoreRevsFile .git-blame-ignore-revs
GitHub автоматично враховує файл .git-blame-ignore-revs у корені репозиторію у своєму інтерфейсі blame.
Коли рядок змінювався багато разів:
git log -L 40,60:app/Services/Billing.php- вся історія змін саме цього фрагмента, з diff кожного кроку;git log -S "1.2"- коміти, що додали чи прибрали конкретний вираз;- в IDE blame вбудований («Annotate with Git Blame» у PhpStorm) з можливістю переходити до попередньої ревізії рядка.
Після rebase чи commit --amend історія локальної гілки розходиться з віддаленою, і звичайний git push відмовляється. git push --force перезаписує віддалену гілку вашою версією безумовно.
Чим це небезпечно: якщо колега встиг відправити в цю гілку свої коміти, force push їх знищить на сервері - ви навіть не побачите, що вони там були.
--force-with-lease перезаписує гілку, лише якщо вона на сервері досі в тому стані, який ви бачили під час останнього fetch. Якщо хтось додав коміти - push відхиляється, і ви спершу інтегруєте їхні зміни.
git rebase main
git push --force-with-lease
Пастка: «той стан, що ви бачили» - це ваш origin/feature. Якщо ви зробили git fetch (чи IDE зробила його у фоні), не подивившись, що прийшло, lease вважатиме нові чужі коміти «баченими». Суворіший варіант - --force-if-includes (Git 2.30+), який вимагає, щоб віддалені коміти були інтегровані в вашу гілку.
Правила команди:
- Force push лише у свої гілки. У
main,develop, релізні гілки - ніколи. - Захищені гілки на GitHub/GitLab забороняють force push і вимагають pull request з рев'ю.
- Якщо над гілкою працює кілька людей - домовитися перед переписуванням історії або обійтися merge.
GitHub Flow - найпоширеніший для веб-застосунків:
mainзавжди готовий до деплою;- кожна задача - коротка гілка від
main; - pull request, рев'ю, CI → злиття в
main→ деплой.
Простий і добре працює з безперервним розгортанням.
Git Flow - для продуктів з версіями й релізами:
main- лише релізи,develop- інтеграція;feature/*відdevelop,release/*для підготовки версії,hotfix/*відmainдля термінових виправлень.
Підходить, коли одночасно підтримують кілька версій (бібліотеки, коробкове ПЗ, мобільні застосунки з рев'ю в сторах). Для веб-сервісу з кількома деплоями на день - надто важкий: багато злиттів, довгоживучі гілки.
Trunk-based development - усі інтегрують зміни в головну гілку дуже часто, щодня чи кілька разів на день:
- гілки живуть години, а не тижні (або коміт одразу в trunk);
- незавершені фічі ховають за feature flags, а не тримають у гілці;
- потрібні сильні автотести й CI.
Мінімум конфліктів злиття і швидкий зворотний зв'язок, але потребує дисципліни й інфраструктури.
Як обирати: веб-продукт з частими деплоями - GitHub Flow чи trunk-based. Кілька підтримуваних версій - щось ближче до Git Flow. Головне - не модель, а короткоживучі гілки: чим довше гілка живе окремо, тим болючіше її злиття.
Worktree - додатковий робочий каталог, прив'язаний до того самого репозиторію. Кожен worktree має свою гілку й свої файли, а історія, об'єкти й налаштування спільні.
git worktree add ../app-hotfix hotfix/invoice-rounding
git worktree add -b review/pr-412 ../app-review origin/feature/export
git worktree list
git worktree remove ../app-hotfix
Сценарій: ви посеред великої задачі з десятком змінених файлів, і терміново потрібно виправити баг у продакшені.
- через
stash: сховати зміни, перемкнутися, виправити, повернутися, дістати зі stash. Каталогvendor/іnode_modules/можуть не відповідати гілці, запущенийnpm run devперезбирає не той код, IDE переіндексовує проєкт; - через worktree: відкрити окремий каталог з гілкою виправлення. Основна робота лишається недоторканою, можна навіть паралельно запустити тести в обох каталогах.
Коли worktree зручний:
- термінові виправлення без переривання поточної роботи;
- рев'ю pull request - перевірити чужу гілку локально, не чіпаючи свою;
- порівняння поведінки двох версій поруч;
- довгі операції (повний прогін тестів, збирання) в одній гілці, поки працюєте в іншій;
- паралельна робота кількох агентів чи скриптів над різними гілками одного репозиторію.
Обмеження:
- одна гілка - один worktree: не можна перемкнутися на гілку, вже відкриту в іншому worktree (Git захищає від двох незалежних змін однієї гілки);
- ігноровані файли не спільні:
vendor/,node_modules/,.envу новому каталозі відсутні - їх доведеться встановити чи скопіювати; - для Laravel: окремий каталог означає окремий
.env; якщо обидва worktree використовують ту саму базу, міграції однієї гілки зачеплять іншу; - видаляйте worktree через
git worktree remove, а неrm -rf- інакше лишаються записи (git worktree pruneїх прибирає).
Порівняно з другим клоном worktree не дублює історію (економить місце й час) і бачить локальні гілки й коміти основного репозиторію без fetch.
Поки ви працюєте над задачею, main просувається. Чим довше гілка живе окремо, тим більше конфліктів і тим вищий ризик, що код, який працює в гілці, зламається після злиття. Тому гілку періодично оновлюють.
Варіант 1 - злити main у гілку:
git fetch origin
git merge origin/main
- історія гілки не переписується - безпечно, навіть якщо в гілці працюють кілька людей;
- push без force;
- конфлікти розв'язуються один раз;
- в історії з'являються коміти злиття
Merge branch 'main' into feature- при squash-злитті pull request вони зникнуть.
Варіант 2 - rebase на main:
git fetch origin
git rebase origin/main
git push --force-with-lease
- лінійна історія: гілка виглядає так, ніби її почали щойно;
- коміти отримують нові хеші, потрібен force push;
- конфлікти розв'язуються для кожного коміту окремо - на довгій гілці це може повторюватися (допомагає
rerere); - небезпечно, якщо гілкою користуються інші: їхні локальні копії розійдуться з вашою.
Як обрати:
| Ситуація | Що краще |
|---|---|
| особиста гілка, коміти ще не рев'ювали | rebase |
| у гілці працюють кілька людей | merge |
| pull request уже на рев'ю | merge - рецензенти бачать, що змінилося після їхніх коментарів (після rebase GitHub гірше показує різницю) |
злиття в main буде через squash |
будь-який, історія гілки все одно зникне |
Як часто: невеликими кроками - щодня чи перед кожним push, а не раз на тиждень. Конфлікт у двох файлах розв'язати легше, ніж у двадцяти.
Найкращий захист від болісного оновлення - короткоживучі гілки: задача на 1-3 дні дає значно менше розбіжностей, ніж гілка, що живе місяць.
На GitHub кнопка «Update branch» у pull request робить merge (чи rebase, якщо обрати). Branch protection з вимогою «Require branches to be up to date» змушує оновлювати гілку перед злиттям.
Lock-файли змінюються майже при кожній зміні залежностей, і дві гілки, що додали по пакету, гарантовано конфліктують. Розв'язувати такий конфлікт вручну, редагуючи JSON, не можна: у composer.lock є content-hash - хеш вмісту composer.json, і ручне злиття майже завжди дає невідповідність або неузгоджене дерево залежностей.
Правильний спосіб для Composer:
- розв'язати конфлікт у
composer.json- він короткий і зрозумілий людині; - взяти
composer.lockз основної гілки; - повторити зміни своєї гілки командою Composer:
git checkout --theirs composer.lock # при rebase - --ours, див. нижче
composer update vendor/added-package # лише пакети, які додала ваша гілка
# або, якщо змінювали лише composer.json без нових пакетів:
composer update --lock
git add composer.json composer.lock
composer update лише для конкретних пакетів, а не всіх: повний composer update оновить усе дерево, і pull request раптом міститиме десятки чужих оновлень.
Для npm:
git checkout --theirs package-lock.json
npm install # перебудує lock з урахуванням вашого package.json
npm також вміє сам розв'язувати конфлікти в package-lock.json: якщо запустити npm install при конфлікті лише в lock-файлі, він об'єднує обидві версії.
Увага до --ours і --theirs при rebase: під час rebase ролі міняються місцями - --ours означає гілку, на яку переносите (тобто main), а --theirs - ваші коміти. Тому при rebase версія main - це --ours.
Як зменшити кількість конфліктів:
- оновлювати залежності окремими невеликими pull request, а не в гілках задач;
- автоматизовані оновлення (Dependabot, Renovate) з частим злиттям;
- перед роботою над задачею оновити гілку з
main.
Перевірка після розв'язання:
composer validate # чи відповідає lock-файл composer.json
composer install # чи встановлюється дерево
php artisan test
Чого не робити: додавати lock-файли в .gitignore, щоб «не конфліктували». Для застосунку lock-файл - гарантія, що продакшен, CI і колеги отримують однакові версії пакетів.
Докладніше в документації: Composer: розв'язання конфліктів злиття
Питання з реальних технічних співбесід - 100 питань у 7 темах, розібраних із відповідями. Нижче - розбивка за рівнями та темами, якщо хочете звузити підготовку.
Готуєтесь до співбесіди не просто так: зараз на сайті 145 відкритих вакансій Laravel і PHP. Переглянути вакансії