Каталог порталу збирає PHP-вакансії з DOU, Djinni і robota.ua, розкладає їх на поля (рівень, фреймворк, формат, англійська, теги) і тримає активними, поки оголошення живе на джерелі. Зараз в ньому 78 активних вакансій від 63 компаній, опубліковані з 3 серпня по 3 вересня 2026. Це вся вибірка, з якою я працюю нижче. Жодних «за нашими спостереженнями»: усе, що тут є, можна перерахувати самому на /jobs.
Перше число задає тон усьому іншому. За рівнями 78 вакансій діляться так: middle 28, senior 28, lead 8, без позначки рівня 8, junior 6.
Шість оголошень, і два з них справді без досвіду
Шість це мало, і я не буду вдавати, що з шести можна порахувати відсотки ринку. Зате шість оголошень читаються цілком, рядок за рядком, і з цього виходить більше користі, ніж з будь-якої агрегації.
Джерела розподілились так: три з robota.ua, два з DOU, одне з Djinni. Шість різних компаній, повторів немає.
Тепер найнеприємніше. З шести junior-оголошень чотири просять або рік комерційного досвіду, або підтверджене портфоліо комерційних проєктів. Одне з них у заголовку каталогу junior, а в тексті прямо називає шуканий рівень Middle: парсер взяв рівень з вимоги «комерційний досвід 1+ роки з PHP», а компанія словом «junior» себе не описує взагалі. Ще одне шукає Strong Junior від року досвіду. Ще одне вимагає «не менше 1 років на позиції Junior Back-end». Четверте просить proven experience і сильне портфоліо.
Без комерційного досвіду в каталозі відкриті дві позиції з шести. Одна з них WordPress в офісі, з вимогами «вища профільна освіта» і «знання платформи WordPress». Друга віддалена, backend або fullstack, де PHP один з п'яти прийнятних варіантів поряд з Python, Java, C# і Node.js, а замість досвіду просять портфоліо чи власні проєкти.
Звідси й практичний висновок про вхід у професію станом на сьогодні: позначка junior у вакансії найчастіше означає «рік уже відпрацював десь».
Чотири теги, які повторюються
Теги в каталозі витягуються з тексту оголошення. По шести junior-вакансіях частоти такі: MySQL 4, REST API 4, Docker 3, Git 3, далі PostgreSQL 2, PHP 2, Microservices 2, і одиничні згадки RabbitMQ, PHPUnit, CI/CD, AWS, Redis, ACF.
Тепер той самий підрахунок по всіх 78 вакансіях: REST API 46, MySQL 42, Docker 42, Git 33, PostgreSQL 27, Redis 24, PHP 8 23, RabbitMQ 20, Microservices 20, CI/CD 20.
Верхівка збігається. Чотири речі, які повторюються в junior-оголошеннях, це чотири речі, які найчастіше повторюються в усьому каталозі незалежно від грейду. Ніякого окремого «junior-стека» в даних немає. Різниця в глибині, а не в переліку.
Що це означає на рівні коду: одного ендпоінта достатньо, щоб показати три з чотирьох пунктів одразу.
final class OrderController
{
public function index(Request $request): JsonResponse
{
$data = $request->validate([
'status' => ['required', Rule::enum(OrderStatus::class)],
'per_page' => ['integer', 'between:1,100'],
]);
$orders = Order::query()
->select(['id', 'number', 'status', 'total_cents', 'created_at'])
->where('status', $data['status'])
->orderByDesc('id')
->paginate($data['per_page'] ?? 20);
return OrderResource::collection($orders)->response();
}
}
Валідація входу окремо від запиту, вибираються тільки потрібні колонки, пагінація замість get() на всю таблицю. Під це потрібен складений індекс (status, id), щоб і фільтр, і сортування читалися з одного індексу, а EXPLAIN на цьому запиті показував index scan замість сортування всієї вибірки. Формулювання з того самого оголошення на Djinni звучить як «розумієш індекси й пишеш чисті запити», і мається на увазі приблизно цей рівень, а не оптимізація планів на мільйонних таблицях.
Чого в junior-оголошеннях немає жодного разу
Redis згадується в 24 вакансіях каталогу і в одній junior. Kafka у 12 і в жодній junior. Kubernetes у 7 і в жодній. High Load, Elasticsearch, GraphQL, Swoole на вході не зустрічаються взагалі.
Середня кількість тегів на junior-оголошення 4.8, по всьому каталогу 5.7. Список вимог на вході не коротший в рази, він коротший на одну позицію, і саме ця позиція зазвичай і є найдорожчою для вивчення.
Черги згадані в одному junior-оголошенні з шести проти 23 з 78 загалом, причому в тому єдиному вони в розділі «буде плюсом» з припискою, що підкажуть. SOLID просить одне junior-оголошення з шести, тоді як по всьому каталогу 29 з 78.
Англійська, формат і гроші
Слово «англійська» чи English не зустрічається в жодному з шести junior-оголошень. У каталозі загалом англійську згадують 32 вакансії з 78, а рівень явно вказаний у 24 (B2 у 12, A2–B1 у 10, C1 у 2). На junior-позиціях поле рівня англійської порожнє в усіх шести.
Читати це варто обережно. Відсутність вимоги в тексті не означає, що на співбесіді не запитають. Але якщо ви відкладаєте відгук, поки не підтягнете англійську, дані каталогу такої відстрочки не виправдовують.
Формат: чотири з шести віддалено, дві в офісі. По всьому каталогу віддалено 55, офіс 11, гібрид 2, у 10 формат не вказаний. Зайнятість: п'ять full-time, одна гіг-контрактом з переходом на повну по мірі розширення задач.
Зарплату вказують дві junior-вакансії з шести (по всьому каталогу 14 з 78), і обидві WordPress: одна фіксовані 795 доларів, друга вилка 800–1300. Два числа це два числа, а не медіана ринку. Рахувати з них нічого не можна, і я не рахую.
Окрема дрібниця, яка суперечить очікуванням: тестове завдання згадане рівно в одному оголошенні з 78, і саме там написано, що воно буває лише у виняткових випадках, коли за кодом і розмовою важко оцінити підхід. Процес найму описують взагалі рідко, тож правильний висновок тут не «тестових не дають», а «наперед ви цього не знаєте».
Symfony на вході не трапляється
За фреймворками 78 активних вакансій діляться так: Laravel 33, Symfony 16, чистий PHP без фреймворку 13, WordPress 6, Yii 6, Drupal 2, Magento 1.
Серед шести junior-позицій: Laravel 4, WordPress 2. Symfony нуль, при шістнадцяти відкритих Symfony-вакансіях у каталозі. Yii, Drupal і Magento на вході теж не трапляються.
Питання не в якості фреймворків, а в тому, куди беруть без досвіду: Laravel і WordPress зараз єдині два входи в каталозі, і якщо ви обираєте, з чого почати заради першої роботи, вибір по цих даних складається сам.
Портфоліо важить більше, ніж перелік
Два junior-оголошення з шести просять посилання на GitHub, два прямо пишуть, що пет-проєкти зараховуються. По всьому каталогу GitHub згадують 10 вакансій з 78, і майже всі згадки на junior-рівні. Це єдина вимога, де вхідні позиції випереджають старші.
Формулювання з одного оголошення я перекажу близько до тексту: додайте посилання на код чи приклади проєктів, навіть пет-проєкт покаже підхід краще, ніж перелік технологій. У репозиторії за хвилину видно рівно дві речі: чи є типи і чи можна це протестувати.
final readonly class ImportPrices
{
public function __construct(
private PriceFeed $feed,
private PriceRepository $prices,
) {}
public function __invoke(string $supplier): ImportResult
{
$imported = 0;
$skipped = 0;
foreach ($this->feed->rows($supplier) as $row) {
if ($row->priceCents <= 0) {
$skipped++;
continue;
}
$this->prices->upsert($supplier, $row->sku, $row->priceCents);
$imported++;
}
return new ImportResult($imported, $skipped);
}
}
Клас нічого не знає ні про HTTP, ні про консоль, залежності приходять через конструктор, тож тест пишеться без бази й без мережі:
it('skips rows with a non-positive price', function () {
$feed = new FakeFeed([
new PriceRow('SKU-1', 1200),
new PriceRow('SKU-2', 0),
]);
$result = (new ImportPrices($feed, new InMemoryPrices()))('supplier-1');
expect($result->imported)->toBe(1)
->and($result->skipped)->toBe(1);
});
Це рівно те, що перевіряють на технічній розмові з переглядом коду, яку описує єдине junior-оголошення з розписаним процесом найму.
Як користуватись цими числами
Каталог оновлюється, і шість junior-вакансій сьогодні не означають шість завтра. Тому корисніше не запам'ятовувати цифри, а відкривати /jobs?level=junior раз на тиждень і дивитися на поточний зріз самому: при такому обсязі нові оголошення видно за хвилину, а конкуренція за них вирішується швидкістю відгуку.
З постійного, що дають дані: MySQL, REST API, Docker і Git повторюються і на вході, і далі, тож це вимоги, які супроводжуватимуть вас наступні кілька років, а зовсім не «мінімум для junior». Redis, черги, Kafka і Kubernetes на вході не питають взагалі, і витрачати на них час до першої роботи немає з чого. Symfony-позицій без досвіду в каталозі немає, тож першою роботою реалістично буде Laravel або WordPress. І головне число: з шести junior-вакансій дві не вимагають комерційного досвіду, а решта чотири вимагають рік або портфоліо. Портфоліо ви можете зробити самі вже цього місяця, рік досвіду ні.