Ранні виходи (Early return)
Все ще закликаю комьюніті до гостьових постів. Сьогодні саме один з таких, дякую автору, @yozhef, за те, що підняв цю тему.
📍 Ранні виходи з функцій чи циклів – це крутий лайфхак, який робить ваш код чистішим, зрозумілішим і простішим для підтримки. Якщо ви відразу виходите з функції, коли умова не відповідає необхідним критеріям, ви економите час як на виконання, так і на розуміння логіки (та і ментальне здоровʼя 😁).
Давайте переглянемо цей код [gist]:
Порівняно з наступним [gist]:
Думаю у early return підхода можна виділити наступні переваги:
🎯 Спрощення логіки — ви уникаєте численних вкладених if-ів, що значно полегшує розуміння логіки.
🛑 Менша вкладеність — менше шансів заплутатися. Як бонус, ваш код стає легшим для читання.
⏩ Швидке завершення. Ранні виходи дозволяють швидше завершити виконання функції, коли вже відомо, що подальші дії непотрібні. Те ж саме стосується і Fail Fast: Як сказали Jim Shore і Martin Fowler, швидке виявлення помилок робить код більш надійним. Якщо помилка трапляється, функція одразу припиняє виконання, що запобігає виконанню зайвих операцій.
🛠️ Простіше модифікувати. Чим менше розгалужень у функції, тим легше вносити зміни або додавати нову логіку без ризику зламати існуючий функціонал.
🧹 Менше залежностей, а відповідно кожна частина коду стає більш ізольованою. Це спрощує рефакторинг та тестування, адже залежності між різними частинами зменшуються.
🔍 Фокус на критичних умовах — Можливість зосередитися на найважливіших умовах виконання коду, підкреслюючи їх значущість і відсіюючи менш критичні сценарії.
Отже, використання ранніх виходів робить ваш код елегантним і ефективним, особливо в умовах складних систем з великою кількістю умов і розгалужень.
#php #lifehack #junior #source
Все ще закликаю комьюніті до гостьових постів. Сьогодні саме один з таких, дякую автору, @yozhef, за те, що підняв цю тему.
📍 Ранні виходи з функцій чи циклів – це крутий лайфхак, який робить ваш код чистішим, зрозумілішим і простішим для підтримки. Якщо ви відразу виходите з функції, коли умова не відповідає необхідним критеріям, ви економите час як на виконання, так і на розуміння логіки (та і ментальне здоровʼя 😁).
Давайте переглянемо цей код [gist]:
function processOrder($order) {
if ($order->isPaid()) {
if ($order->hasValidShippingAddress()) {
if ($order->isInStock()) {
// Логіка обробки замовлення
return "Order processed successfully.";
} else {
return "Order cannot be processed: Out of stock.";
}
} else {
return "Order cannot be processed: Invalid shipping address.";
}
} else {
return "Order cannot be processed: Payment pending.";
}
}
Порівняно з наступним [gist]:
function processOrder($order) {
if (!$order->isPaid()) {
return "Order cannot be processed: Payment pending.";
}
if (!$order->hasValidShippingAddress()) {
return "Order cannot be processed: Invalid shipping address.";
}
if (!$order->isInStock()) {
return "Order cannot be processed: Out of stock.";
}
// Логіка обробки замовлення
return "Order processed successfully.";
}
Думаю у early return підхода можна виділити наступні переваги:
🎯 Спрощення логіки — ви уникаєте численних вкладених if-ів, що значно полегшує розуміння логіки.
🛑 Менша вкладеність — менше шансів заплутатися. Як бонус, ваш код стає легшим для читання.
⏩ Швидке завершення. Ранні виходи дозволяють швидше завершити виконання функції, коли вже відомо, що подальші дії непотрібні. Те ж саме стосується і Fail Fast: Як сказали Jim Shore і Martin Fowler, швидке виявлення помилок робить код більш надійним. Якщо помилка трапляється, функція одразу припиняє виконання, що запобігає виконанню зайвих операцій.
🛠️ Простіше модифікувати. Чим менше розгалужень у функції, тим легше вносити зміни або додавати нову логіку без ризику зламати існуючий функціонал.
🧹 Менше залежностей, а відповідно кожна частина коду стає більш ізольованою. Це спрощує рефакторинг та тестування, адже залежності між різними частинами зменшуються.
🔍 Фокус на критичних умовах — Можливість зосередитися на найважливіших умовах виконання коду, підкреслюючи їх значущість і відсіюючи менш критичні сценарії.
Отже, використання ранніх виходів робить ваш код елегантним і ефективним, особливо в умовах складних систем з великою кількістю умов і розгалужень.
#php #lifehack #junior #source
👍53🔥6👀2⚡1🙊1
Сьомий випуск Fwdays PHP Talks
Разом з Олегом Зінченко та Владиславом Яришом поговорили про болючі теми:
💵 Оплачувані і безкоштовні тестові
🤯 Чому варто вивчати golang?
📈 Як python зайняв перше місце в рейтингу?
🔥 Куди рухається PHP і як збільшити його популярніть?
Доєднутесь дивитись (або просто слухати) та діліться своїми думками 🍻
Разом з Олегом Зінченко та Владиславом Яришом поговорили про болючі теми:
💵 Оплачувані і безкоштовні тестові
🤯 Чому варто вивчати golang?
📈 Як python зайняв перше місце в рейтингу?
Доєднутесь дивитись (або просто слухати) та діліться своїми думками 🍻
Media is too big
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥4🙊2🗿1
Пропускна здатність обмежена
Гігабітний інтернет, це багато, чи мало? Фактично це дає нам змогу обмінюватись інформацією зі швидкістю 125 МБ/сек – у теорії. Але в реальності, через всі накладні витрати, ця цифра може легко падати до 60 МБ/сек, навіть коли обидві сторони (клієнт та сервер) знаходяться на одному континенті.
❗️ Важливо розуміти, що кількість даних, котрими ми обмінюємось постійно і стрімко зростає. Відео контент вже став стандартом на сьогоднішній день, хоча 15 років тому я в браузері відключав завантаження картинок, аби текстовий контент завантажувався швидше. При цьому пропускна здатність збільшується не так швидко, як би нам хотілось.
Припустимо, що у вас є середній розмір DTO близько 5 КБ, а ваша система обслуговує 100 RPS (requests per second). Це означає, що за секунду передається приблизно 500 КБ даних – що досить непогано, враховуючи потенційне обмеження в 60 МБ.
📍 Не всі хмарні рішення готові надавати 1 Гбіт "із коробки". Ваша пропускна здатність може бути легко обмежена 200 Мбіт (без додаткових витрат), а DTO важити далеко не 5 КБ. За допомогою ORM ми починаємо тягнути купу даних, навіть якщо вони не потрібні. Наприклад, коли пишемо API для інтернет-магазину, то контролер може повертати об’єкт Order із усіма деталями: інформацією про користувача, товари, опис кожного з них, метадані для фільтрів, дані про доставку, оплату. В результаті серіалізації такого обʼєкту на виході отримуємо JSON розміром вже не 5 КБ, а 400 КБ.
🤓 Можна легко підрахувати, що при 200 Мбіт (25 МБ/c) зазначеної пропускної здатності ми витримати всього 60 RPS.
Звісно, можна просто доплатити і розширити канал, взяти ще один сервер, розмежувати якимось чином network і т.д. Проте ми будемо платити за обсяги даних, що передаємо.
👉 На цьому етапі виникає конфлікт інтересів. З одного боку, ми говорили про те, що хочемо завантажувати більше даних за раз, щоб отримувати все і якось боротись з latency. З іншого боку, це збільшує навантаження на мережу, що може призвести до затримок і неефективного використання пропускної здатності.
👍 Отже нам потрібен баланс. Цей баланс залежить від конкретного сценарію. В цьому нам допоможуть Bounded Context, Read/Write Model а також Aggregate.
#backend #architecture #middle #source
Гігабітний інтернет, це багато, чи мало? Фактично це дає нам змогу обмінюватись інформацією зі швидкістю 125 МБ/сек – у теорії. Але в реальності, через всі накладні витрати, ця цифра може легко падати до 60 МБ/сек, навіть коли обидві сторони (клієнт та сервер) знаходяться на одному континенті.
❗️ Важливо розуміти, що кількість даних, котрими ми обмінюємось постійно і стрімко зростає. Відео контент вже став стандартом на сьогоднішній день, хоча 15 років тому я в браузері відключав завантаження картинок, аби текстовий контент завантажувався швидше. При цьому пропускна здатність збільшується не так швидко, як би нам хотілось.
Припустимо, що у вас є середній розмір DTO близько 5 КБ, а ваша система обслуговує 100 RPS (requests per second). Це означає, що за секунду передається приблизно 500 КБ даних – що досить непогано, враховуючи потенційне обмеження в 60 МБ.
100 RPS * 5 КБ = 500 КБ/с
📍 Не всі хмарні рішення готові надавати 1 Гбіт "із коробки". Ваша пропускна здатність може бути легко обмежена 200 Мбіт (без додаткових витрат), а DTO важити далеко не 5 КБ. За допомогою ORM ми починаємо тягнути купу даних, навіть якщо вони не потрібні. Наприклад, коли пишемо API для інтернет-магазину, то контролер може повертати об’єкт Order із усіма деталями: інформацією про користувача, товари, опис кожного з них, метадані для фільтрів, дані про доставку, оплату. В результаті серіалізації такого обʼєкту на виході отримуємо JSON розміром вже не 5 КБ, а 400 КБ.
🤓 Можна легко підрахувати, що при 200 Мбіт (25 МБ/c) зазначеної пропускної здатності ми витримати всього 60 RPS.
Звісно, можна просто доплатити і розширити канал, взяти ще один сервер, розмежувати якимось чином network і т.д. Проте ми будемо платити за обсяги даних, що передаємо.
👉 На цьому етапі виникає конфлікт інтересів. З одного боку, ми говорили про те, що хочемо завантажувати більше даних за раз, щоб отримувати все і якось боротись з latency. З іншого боку, це збільшує навантаження на мережу, що може призвести до затримок і неефективного використання пропускної здатності.
👍 Отже нам потрібен баланс. Цей баланс залежить від конкретного сценарію. В цьому нам допоможуть Bounded Context, Read/Write Model а також Aggregate.
#backend #architecture #middle #source
👍23🔥2
Bounded Context
Перший крок — варто уникати створення єдиної, монолітної доменної моделі для всієї системи. Намагайтесь розділити модель і ваші обʼєкти на менші частини, кожна з яких відповідає за конкретний сценарій або контекст. Наприклад, якщо у нас є система для обробки замовлень інтернет магазину, то є обʼєкт (Order), але в різних частинах системи це слово означає різне:
Для створення замовлення важливі товари й ціна
Для оплати — номер картки й статус транзакції
Для доставки — адреса й дата
Обмежений контекст — це коли ми ділимо модель на маленькі шматочки, кожен із яких живе окремо і відповідає за конкретну задачу.
⁉️ Як це працює?
Cтворюємо окремі класи для кожного контексту. Наприклад:
OrderCreation — для створення замовлення
OrderPayment — для оплати
OrderDelivery — для доставки
Кожен із них має тільки те, що йому потрібно, і не знає про існування інших.
[golang] [php] [python] [nodejs]
📍Моделі (обʼєкти, сервіси, ентіті, хендлери і т.д.) в одному обмеженому контексті мають бути тісно пов’язані (cohesion) й працювати разом для однієї мети. Наприклад, у контексті OrderCreation це може бути додавання, видалення й оновлення товарів в замолені, бо це одна бізнес-здібність.
📍В той же час контекст не має сильно залежати від інших (loose coupling). Наприклад, OrderDelivery оперує саме OrderId а не цілим Order і зовсім нічого не знає про деталі оплати.
Переваги такого підходу:
1. Код стає простішим: кожен клас маленький і зрозумілий.
2. Легше тестувати: можна окремо перевіряти кожну частинку, не чіпаючи іншу.
3. Передаємо менше даних. Наприклад, OrderPayment випадково не потягне за собою адресу доставки, бо вона йому не потрібна.
❗️ Розділені контексти - це не раз і назавжди. З часом, коли проект росте, ви можете і маєте переглядати їх. В моноліті кордони між контекстами можуть бути малопомітні, адже виклик сервіса із сусідньої папки не викликає ніяких проблем. Проте варто за цим слідкувати. Як мінімум впровадити архітектурні тести (deptrac) з описаними правилами, котрий буде запускатись разом з тестами і статичним аналізатором.
#backend #architecture #middle #source
Перший крок — варто уникати створення єдиної, монолітної доменної моделі для всієї системи. Намагайтесь розділити модель і ваші обʼєкти на менші частини, кожна з яких відповідає за конкретний сценарій або контекст. Наприклад, якщо у нас є система для обробки замовлень інтернет магазину, то є обʼєкт (Order), але в різних частинах системи це слово означає різне:
Для створення замовлення важливі товари й ціна
Для оплати — номер картки й статус транзакції
Для доставки — адреса й дата
Обмежений контекст — це коли ми ділимо модель на маленькі шматочки, кожен із яких живе окремо і відповідає за конкретну задачу.
⁉️ Як це працює?
Cтворюємо окремі класи для кожного контексту. Наприклад:
OrderCreation — для створення замовлення
OrderPayment — для оплати
OrderDelivery — для доставки
Кожен із них має тільки те, що йому потрібно, і не знає про існування інших.
[golang] [php] [python] [nodejs]
final readonly class OrderCreation {
/**
* @param OrderItem[] $items
*/
public function __construct(
private OrderId $orderId,
private UserId $userId,
private array $items
) {
}
}
// Контекст оплати замовлення
final readonly class OrderPayment {
public function __construct(
private OrderId $orderId,
private Money $sum
)
{
}
}
// Контекст доставки
final readonly class OrderDelivery {
public function __construct(
private OrderId $orderId,
private Address $address
)
{
}
}
// Використання
$orderCreation = new OrderCreation(
new OrderId($this->orderIdGenerator->generate()),
new UserId($this->userId),
[
new OrderItem(new ProductId('notebook'), $quantity),
new OrderItem(new ProductId('gamepad'), $quantity),
]
);
$orderPayment = new OrderPayment(
$orderCreation->orderId(),
new Money($this->total)
);
$orderDelivery = new OrderDelivery(
$orderCreation->orderId(),
new Address(
$this->city,
$this->street,
$this->house
)
);
📍Моделі (обʼєкти, сервіси, ентіті, хендлери і т.д.) в одному обмеженому контексті мають бути тісно пов’язані (cohesion) й працювати разом для однієї мети. Наприклад, у контексті OrderCreation це може бути додавання, видалення й оновлення товарів в замолені, бо це одна бізнес-здібність.
📍В той же час контекст не має сильно залежати від інших (loose coupling). Наприклад, OrderDelivery оперує саме OrderId а не цілим Order і зовсім нічого не знає про деталі оплати.
Переваги такого підходу:
1. Код стає простішим: кожен клас маленький і зрозумілий.
2. Легше тестувати: можна окремо перевіряти кожну частинку, не чіпаючи іншу.
3. Передаємо менше даних. Наприклад, OrderPayment випадково не потягне за собою адресу доставки, бо вона йому не потрібна.
❗️ Розділені контексти - це не раз і назавжди. З часом, коли проект росте, ви можете і маєте переглядати їх. В моноліті кордони між контекстами можуть бути малопомітні, адже виклик сервіса із сусідньої папки не викликає ніяких проблем. Проте варто за цим слідкувати. Як мінімум впровадити архітектурні тести (deptrac) з описаними правилами, котрий буде запускатись разом з тестами і статичним аналізатором.
#backend #architecture #middle #source
👍31🔥5
Aggregate
Що робити, коли нам все таки потрібні різні частинки, бо вони приймають участь в одній операції? Наприкад, створення Invoice для того самого Order (продовжуємо думку попереднього поста) може вимагати як особистих даних користувача, так і повного списку товарів (щоб їх там відобразити), а також загальної суми для транзакції. Тут на поміч приходить агрегат.
👉 Агрегат — група пов’язаних об’єктів домену. Він складається з однієї або декількох сутностей (а інколи й об’єктів-значень), які логічно пов’язані між собою.
У нашому випадку, коли для створення інвойсу потрібні дані користувача, список товарів і сума, агрегат має зібрати все це разом і гарантувати, що зміни відбуватимуться транзакційно. Наприклад, якщо додати новий товар, то загальна сума автоматично оновиться.
❗️ Агрегат має корінь (aggregate root), через який відбуваються всі операції. Він відповідає за забезпечення інваріантів (бізнес-правил), надання методів для доступу та зміни стану, щоб зовнішні виклики не могли порушити ту саму узгодженість. Це означає, що замість того, щоб окремо взаємодіяти з обʼєктами даних користувача, товарами й сумою, ви працюєте з одним об’єктом, який уже все це тримає в узгодженому стані.
[golang] [php] [python] [nodejs]
👉 Коли вам потрібно створити інвойс, ви викликаєте createInvoice(), і агрегат повертає об’єкт Invoice із усіма потрібними даними. Вам не доводиться вручну збирати ці частинки з різних джерел чи турбуватися про їхню узгодженість — агрегат це робить за вас.
Підсумуємо:
– Всі зміни в агрегаті виконуються узгоджено. Після завершення транзакції стан усіх сутностей агрегата має бути консистентним.
– Агрегати визначають область транзакції – операції над ними мають виконуватися атомарно. Наприклад, якщо ми щось зберігаємо, або дістаємо зі storage, то ми маємо це робити з усім агрегатом. Дані не можуть бути оновлені частково.
– Зовнішні об’єкти можуть взаємодіяти лише з агрегатним коренем, що дозволяє зберігати внутрішню цілісність агрегата.
❗️Агрегат не повинен містити інших агрегатів. Кожен агрегат має свою власну область відповідальності. Якщо вам потрібно з якоїсь причини повʼязати агрегати — використовуйте ідентифікатори для того щоб зробити посилання на інший агрегат.
#backend #architecture #middle #source
Що робити, коли нам все таки потрібні різні частинки, бо вони приймають участь в одній операції? Наприкад, створення Invoice для того самого Order (продовжуємо думку попереднього поста) може вимагати як особистих даних користувача, так і повного списку товарів (щоб їх там відобразити), а також загальної суми для транзакції. Тут на поміч приходить агрегат.
👉 Агрегат — група пов’язаних об’єктів домену. Він складається з однієї або декількох сутностей (а інколи й об’єктів-значень), які логічно пов’язані між собою.
У нашому випадку, коли для створення інвойсу потрібні дані користувача, список товарів і сума, агрегат має зібрати все це разом і гарантувати, що зміни відбуватимуться транзакційно. Наприклад, якщо додати новий товар, то загальна сума автоматично оновиться.
❗️ Агрегат має корінь (aggregate root), через який відбуваються всі операції. Він відповідає за забезпечення інваріантів (бізнес-правил), надання методів для доступу та зміни стану, щоб зовнішні виклики не могли порушити ту саму узгодженість. Це означає, що замість того, щоб окремо взаємодіяти з обʼєктами даних користувача, товарами й сумою, ви працюєте з одним об’єктом, який уже все це тримає в узгодженому стані.
[golang] [php] [python] [nodejs]
// Клас для даних користувача
final class UserData {
public function __construct(
public string $name,
public Address $address
) {}
}
/**
* @param Product[] $items
*/
final class Invoice {
public function __construct(
public OrderId $orderId,
public UserData $user,
public array $items,
public Money $total
) {}
}
// Агрегат для замовлення
final class OrderAggregate {
private OrderId $orderId;
private UserData $user;
private array $items;
private Money $total;
public function __construct(OrderId $orderId, UserData $user, array $items) {
$this->orderId = $orderId;
$this->user = $user;
$this->items = $items;
$this->calculateTotal();
}
// Розрахунок загальної суми
private function calculateTotal(): void {
$this->total = array_sum(array_map(fn($item) => $item->price * $item->amount, $this->items));
}
// Додавання нового товару
public function addItem(string $name, Money $price, int $quantity): void {
$this->items[] = new Product($name, $price, $quantity);
$this->calculateTotal();
}
// Створення інвойсу
public function createInvoice(): Invoice {
return new Invoice($this->orderId, $this->user, $this->items, $this->total);
}
}
// Використання
$user = new UserData('Іван Петренко', new Address('Київ', 'вул. Шевченка', '1'));
$order = new OrderAggregate('123', $user, [new Product('Ноутбук', 20000, 1)]);
$order->addItem('Мишка', 500, 2);
$invoice = $order->createInvoice();
echo \sprintf(‘Інвойс для замовлення %s, користувач: %s, сума: %s грн\n’, $invoice->orderId, $invoice->user->name, $invoice->total);
👉 Коли вам потрібно створити інвойс, ви викликаєте createInvoice(), і агрегат повертає об’єкт Invoice із усіма потрібними даними. Вам не доводиться вручну збирати ці частинки з різних джерел чи турбуватися про їхню узгодженість — агрегат це робить за вас.
Підсумуємо:
– Всі зміни в агрегаті виконуються узгоджено. Після завершення транзакції стан усіх сутностей агрегата має бути консистентним.
– Агрегати визначають область транзакції – операції над ними мають виконуватися атомарно. Наприклад, якщо ми щось зберігаємо, або дістаємо зі storage, то ми маємо це робити з усім агрегатом. Дані не можуть бути оновлені частково.
– Зовнішні об’єкти можуть взаємодіяти лише з агрегатним коренем, що дозволяє зберігати внутрішню цілісність агрегата.
❗️Агрегат не повинен містити інших агрегатів. Кожен агрегат має свою власну область відповідальності. Якщо вам потрібно з якоїсь причини повʼязати агрегати — використовуйте ідентифікатори для того щоб зробити посилання на інший агрегат.
#backend #architecture #middle #source
👍29🔥3🗿2🙊1
Read, Write, Partial Update
Наступна відома проблема, коли ми намагаємось використовувати один і той самий обʼєкт як для читання (наприклад представлення на frontend) так і для запису (створення, оновлення).
👉 Візьмемо іншу сутність в рамках нашого створення замолення — Customer. Побудуємо його з низки Value Objects щоб гарантувати, що дані завжди валідні.
Які ж можуть виникнути проблеми?
1️⃣ Щось, що ми не хочемо відображати
Якщо для відображення ми будемо використовувати той самий обʼєкт Customer, існує ризик, що у відповідь потраплять зайві дані.
Наприклад захешований пароль
Так, можемо по місцю зробити щось на кшталт:
2️⃣ Зміна правил валідації
Їдемо далі. Наші перевірки в Value Objects можуть змінюватись з часом. В якийсь момент, ми можемо зробити їх більш жорсткими. В базі даних можуть зберігатись записи, котрі можуть не пройти правила нової валідації, якщо ми будемо використовувати один і той самий обʼєкт для читання і для запису. Тут починаються трюки з Reflection. Вже не так райдужно.
3️⃣ Додаткові дані для відображення
Наступна проблема, коли для відображення нам потрібна інформація, котра не міститься в поточному обʼєкті. Наприклад, кількість замовлень.
Варіант робочий, проте ми тут робимо 2 запити до бази даних, хоча потенційно можна зробити один.
4️⃣ Чи дійсно створення та оновлення це одне і те саме?
При створенні нового Customer ми очікуємо email, name та password. Але, наприклад, при зміні імені, що ми маємо передавати в полі пароля. Чи навпаки, при зміні пароля нам потрібна специфічна логіка (запит на зміну, надсилання токена тощо).
🤓 Щоб вирішити ці проблеми, доцільно використовувати окремі моделі для різних операцій. Наприклад:
Або для оновлення даних:
✏️ Write model забезпечує дотримання інваріантів і консистентність даних. Оскільки тут ми не паримось як саме будемо виводити ці дані - це дозволяє нам робити обʼєкт маленьким і конкретним.
📖 Read model дає можливість отримати всю необхідну інформацію, потрібну для конкретного відображення чи перегляду.
Не бійтеся створювати окремі представлення для різних операцій. Ваш код стане більш стабільним, логічни та простим для підтримки.
#backend #architecture #middle #source
Наступна відома проблема, коли ми намагаємось використовувати один і той самий обʼєкт як для читання (наприклад представлення на frontend) так і для запису (створення, оновлення).
👉 Візьмемо іншу сутність в рамках нашого створення замолення — Customer. Побудуємо його з низки Value Objects щоб гарантувати, що дані завжди валідні.
$customer = new Customer(
new CustomerId(uniqid()),
new Email($request->email),
new Name($request->name)),
new Password($request->password))
);
$repository->save($customer);
Які ж можуть виникнути проблеми?
1️⃣ Щось, що ми не хочемо відображати
Якщо для відображення ми будемо використовувати той самий обʼєкт Customer, існує ризик, що у відповідь потраплять зайві дані.
interface CustomerRepository
{
public function save(Customer $customer): void;
public function get(string $email): Customer;
}
Наприклад захешований пароль
{
"customerId": "5f2b...",
"email": "jane@doe.com",
"name": "Jane Doe",
"password": "$2y$10$..."
}
Так, можемо по місцю зробити щось на кшталт:
final readonly class Customer
{
// ...
public function serialize(): array
{
return [
'email' => $this->email,
'name' => $this->name,
];
}
}
2️⃣ Зміна правил валідації
Їдемо далі. Наші перевірки в Value Objects можуть змінюватись з часом. В якийсь момент, ми можемо зробити їх більш жорсткими. В базі даних можуть зберігатись записи, котрі можуть не пройти правила нової валідації, якщо ми будемо використовувати один і той самий обʼєкт для читання і для запису. Тут починаються трюки з Reflection. Вже не так райдужно.
3️⃣ Додаткові дані для відображення
Наступна проблема, коли для відображення нам потрібна інформація, котра не міститься в поточному обʼєкті. Наприклад, кількість замовлень.
[
...$customerRepository->getById($customerId),
...['orders_count' => $orderRepository->count($customerId)]
]
Варіант робочий, проте ми тут робимо 2 запити до бази даних, хоча потенційно можна зробити один.
4️⃣ Чи дійсно створення та оновлення це одне і те саме?
При створенні нового Customer ми очікуємо email, name та password. Але, наприклад, при зміні імені, що ми маємо передавати в полі пароля. Чи навпаки, при зміні пароля нам потрібна специфічна логіка (запит на зміну, надсилання токена тощо).
interface CustomerRepository
{
public function save(Customer $user): void;
public function update(Customer $user): void;
}
// Updating name
$customer = new Customer(
$request->email,
$request->name,
'а що тут робити з паролем?',
);
$repository->update($customer);
🤓 Щоб вирішити ці проблеми, доцільно використовувати окремі моделі для різних операцій. Наприклад:
final readonly class CustomerRead
{
public function __construct(
public CustomerId $customerId,
public Email $email,
public Name $name,
public int $orderCount,
) {}
}
interface CustomerReadRepository
{
public function get(CustomerId $customerId): CustomerRead;
}
Або для оновлення даних:
final readonly class CustomerNameUpdate
{
public function __construct(
public CustomerId $customerId,
public Name $name,
)
}
interface CustomerRepository
{
public function save(Customer $customer): void;
public function updateData(CustomerNameUpdate $customerNameUpdate): void;
public function updatePassword(CustomerPasswordUpdate $customerPasswordUpdate): void;
}
✏️ Write model забезпечує дотримання інваріантів і консистентність даних. Оскільки тут ми не паримось як саме будемо виводити ці дані - це дозволяє нам робити обʼєкт маленьким і конкретним.
📖 Read model дає можливість отримати всю необхідну інформацію, потрібну для конкретного відображення чи перегляду.
Не бійтеся створювати окремі представлення для різних операцій. Ваш код стане більш стабільним, логічни та простим для підтримки.
#backend #architecture #middle #source
👍32🔥5🙊3⚡1
Знову про індекси
На моїй практиці 7 з 10 інженерів відповідають приблизно в такому форматі. Можливо тільки в мене така статистика, але пропоную зануритись трошки глибше.
❗️В 90% випадках, незалежно від бази даних (MySQL, Postgres, Mongo) для забезпечення своїх потреб ми будемо використовувати B-Tree або B+Tree індекси.
«Це ж бінарні дерева!» вигукують інженери. І так і ні.
👉 Ця структура - дійсно дерево, і замість того, щоб виконувати пошук по списку і перебирати всі дані O(n), змінивши структуру і розклавши дані у дерево ми будемо виконувати операцію зі складністю ~ O(log n). Якщо дерево бінарне, то і пошук буде бінарним.
❓ Чому важливо, що це не просто бінарне дерево?
При додаванні нового елемента в бінарне дерево - важливий порядок додавання. Перший елемент стає коренем дерева і від нього починають будуватись всі гілки.
❗️ Це неминуче призводить до того, що деякі гілки будуть значно довші (вищі) ніж інші. Така структура не може гарантувати швидкість пошуку по всі таблиці з однаковою ефективністю. Ось приклад незбалансованого дерева (в гіршому випадку):
Таке дерево фактично стає зв'язним списком з складністю пошуку O(n).
👍 Для вирішення цієї проблеми придумали збалансовані дерева. Тут при кожній операції вставки алгоритм обертає значення таким чином, щоб як умога довше забезпечити однакову висоту по всьому дереву. Обертання - досить дорога операція, і це сильно сповільнює вставку.
✅️️️️️️️ Інженери пішли далі і сьогодні ми маємо такі різновиди збалансованих дерев як B-Tree та B+Tree. В них при операції вставки ми не одразу породжуємо нову ноду, а намагаємось вставити дані за певним алгоритмом, щоб зберегти висоту. Це породжує декілька значень на рівні однієї ноди:
🤓 Але про них ми більш подробно поговоримо в наступному пості. Отже, головний посил, що індекси - не магія і не просто "якась штука збоку". Це конкретні структури даних, що пришвидшують пошук.
#database #middle
- Що робити, якщо ваш запит в БД відпрацьовує повільно?
- Ну я б спробував(ла) додати індекс.
- Чудово, а чому індекс пришвидшує пошук?
- Нуууу, просто це якась відсортована штука збоку. Ось вона відсортована, пошук швидше.
На моїй практиці 7 з 10 інженерів відповідають приблизно в такому форматі. Можливо тільки в мене така статистика, але пропоную зануритись трошки глибше.
❗️В 90% випадках, незалежно від бази даних (MySQL, Postgres, Mongo) для забезпечення своїх потреб ми будемо використовувати B-Tree або B+Tree індекси.
«Це ж бінарні дерева!» вигукують інженери. І так і ні.
👉 Ця структура - дійсно дерево, і замість того, щоб виконувати пошук по списку і перебирати всі дані O(n), змінивши структуру і розклавши дані у дерево ми будемо виконувати операцію зі складністю ~ O(log n). Якщо дерево бінарне, то і пошук буде бінарним.
❓ Чому важливо, що це не просто бінарне дерево?
При додаванні нового елемента в бінарне дерево - важливий порядок додавання. Перший елемент стає коренем дерева і від нього починають будуватись всі гілки.
Додаємо числа: 10, 5, 15, 3, 7, 12, 20
Крок 1: Додаємо 10 (корінь)
10
Крок 2: Додаємо 5 (менше 10, йде ліворуч)
10
/
5
Крок 3: Додаємо 15 (більше 10, йде праворуч)
10
/ \
5 15
Крок 4: Додаємо 3 (менше 5, йде ліворуч від 5)
10
/ \
5 15
/
3
Крок 5: Додаємо 7 (більше 5, йде праворуч від 5)
10
/ \
5 15
/ \
3 7
Крок 6: Додаємо 12 (менше 15, йде ліворуч від 15)
10
/ \
5 15
/ \ /
3 7 12
Крок 7: Додаємо 20 (більше 15, йде праворуч від 15)
10
/ \
5 15
/ \ / \
3 7 12 20
❗️ Це неминуче призводить до того, що деякі гілки будуть значно довші (вищі) ніж інші. Така структура не може гарантувати швидкість пошуку по всі таблиці з однаковою ефективністю. Ось приклад незбалансованого дерева (в гіршому випадку):
Додаємо числа в порядку зростання: 1, 2, 3, 4, 5, 6, 7
1
\
2
\
3
\
4
\
5
\
6
\
7
Таке дерево фактично стає зв'язним списком з складністю пошуку O(n).
👍 Для вирішення цієї проблеми придумали збалансовані дерева. Тут при кожній операції вставки алгоритм обертає значення таким чином, щоб як умога довше забезпечити однакову висоту по всьому дереву. Обертання - досить дорога операція, і це сильно сповільнює вставку.
Припустимо, у нас є незбалансоване дерево, яке "виросло" вліво:
30
/
20
/ \
10 25
Необхідно виконати праве обертання навколо кореня (30):
Крок 1: Беремо вузол 20 як новий корінь
Крок 2: Старий корінь (30) стає правим нащадком нового кореня
Крок 3: Правий нащадок нового кореня (якщо є) стає лівим нащадком
старого кореня (25 стає зліва)
Результат:
20
/ \
25 30
/
10
Тепер дерево збалансоване
Розглянемо складніший випадок з подвійним обертанням. Дано:
30
/
10
\
20
Тут праве обертання не виправить ситуацію. Потрібне подвійне обертання:
Спочатку ліве обертання навколо 10:
30
/
20
/
10
Потім праве обертання навколо 30:
20
/ \
10 30
✅️️️️️️️ Інженери пішли далі і сьогодні ми маємо такі різновиди збалансованих дерев як B-Tree та B+Tree. В них при операції вставки ми не одразу породжуємо нову ноду, а намагаємось вставити дані за певним алгоритмом, щоб зберегти висоту. Це породжує декілька значень на рівні однієї ноди:
[15, 50]
/ | \
[5] [20] [70]
🤓 Але про них ми більш подробно поговоримо в наступному пості. Отже, головний посил, що індекси - не магія і не просто "якась штука збоку". Це конкретні структури даних, що пришвидшують пошук.
#database #middle
👍50🔥8🗿1
B-Tree та B+Tree індекси
👉 Саме вони є найбільш розповсюдженими в нашому повсякденному житті. Майже всі популярні БД (PostgreSQL, MySQL/InnoDB, SQLite, MongoDB і т.д.) використовують B+Tree для зберігання індексів. Як зазначено в попередньому пості, це різновиди збаланосованих дерев. Головна особливість в тому, що кожен вузол може мати більше двох нащадків (на відміну від бінарного дерева), а також може мати кілька ключів (значень) в одному вузлі.
Порядок B-Tree (зазвичай позначається як m) визначає максимальну кількість нащадків, які може мати вузол.
- Кожна нода (крім кореня) має від ceil(m/2)-1 до m-1 ключів
- Корінь має від 1 до m-1 ключів
- Нода з k ключами має k+1 дочірніх нод
❗️У нашому прикладі ми використовуємо B-Tree порядку 3, це значить, що може бути до 2 ключів у кожному вузлі, і до 3 нащадків для кожного вузла відповідно.
📍 B+Tree відрізняється від B-Tree тим, що всі дані зберігаються тільки в листкових вузлах, які додатково пов'язані між собою як зв'язний список, що значно пришвидшує послідовне читання а значить і кращу продуктивність при пошуку близьких значень.
Алгоритм вставки:
❓ "Перебудова індексу", або чому вставки такі дорогі?
🔥 Основна причина популярності цих індексів - оптимізація роботи з диском. Диск читається блоками (вони ж сторінки індексу фіксованого розміру, зазвичай 4KB, 8KB або 16KB залежно від БД), і B-дерева чудово це використовують, зберігаючи багато ключів в одному вузлі.
Fill Factor (фактор заповнення) - параметр, який визначає, наскільки заповненими будуть сторінки індексу при їх створенні. Наприклад, fill factor 70% означає, що сторінки заповнюються на 70%, залишаючи 30% вільного місця для майбутніх вставок.
Коли сторінка індексу заповнюється повністю (через вставки даних), відбувається операція розщеплення сторінки (page split):
1️⃣ Створюється нова сторінка
2️⃣ Приблизно половина даних переміщується в нову сторінку
3️⃣ Середній ключ "піднімається" до батьківської сторінки
4️⃣ Батьківська сторінка оновлює свої посилання
❗️Це і є супер дорога операція, бо вимагає виділення нової сторінки на диску, переміщення даних, оновлення посилань, можливе розщеплення батьківських сторінок (каскадний ефект).
Також налаштування fill factor може підвищити продуктивність бази даних:
Для таблиць з частими вставками — низький fill factor (70-80%)
Для таблиць тільки для читання — високий fill factor (90-100%)
👍 Сподіваюсь, у світі стало трошки менше магії, і трошки більше розуміння того, як працюють системи, якими ми користуємось щодня.
#database #middle
👉 Саме вони є найбільш розповсюдженими в нашому повсякденному житті. Майже всі популярні БД (PostgreSQL, MySQL/InnoDB, SQLite, MongoDB і т.д.) використовують B+Tree для зберігання індексів. Як зазначено в попередньому пості, це різновиди збаланосованих дерев. Головна особливість в тому, що кожен вузол може мати більше двох нащадків (на відміну від бінарного дерева), а також може мати кілька ключів (значень) в одному вузлі.
Порядок B-Tree (зазвичай позначається як m) визначає максимальну кількість нащадків, які може мати вузол.
- Кожна нода (крім кореня) має від ceil(m/2)-1 до m-1 ключів
- Корінь має від 1 до m-1 ключів
- Нода з k ключами має k+1 дочірніх нод
❗️У нашому прикладі ми використовуємо B-Tree порядку 3, це значить, що може бути до 2 ключів у кожному вузлі, і до 3 нащадків для кожного вузла відповідно.
📍 B+Tree відрізняється від B-Tree тим, що всі дані зберігаються тільки в листкових вузлах, які додатково пов'язані між собою як зв'язний список, що значно пришвидшує послідовне читання а значить і кращу продуктивність при пошуку близьких значень.
Алгоритм вставки:
Коли нода заповнюється і не може вмістити новий ключ, при черговій вставці відбувається операція ділення і ми створюємо новий рівень з існуючих даних.
[5, 15]
/ | \
[3] [10,20,30] [40]
1. У нас є переповнена нода: [10, 20, 30]
2. Медіана: 20
3. Розділяємо на: [10] і [30]
4. 20 піднімається до батьківської ноди
[5, 15, 20]
/ / \ \
[3] [10] [30] [40]
Продовжуємо процес, бо [5, 15, 20] теж переповнена:
1. Медіана: 15
2. Розділяємо на: [5] і [20]
3. 15 піднімається вище
Припустимо, що раніше наше дерево мало корінь з одним елементом [50], до якого ми додаємо 15:
Проміжний результат:
[15, 50]
/ | \
[5] [20] [...]
Остаточно структура виглядає так:
[15, 50]
/ | \
[5] [20] [...]
/ \ / | \
[3] [10] [...] [30] [40]
При цьому складність пошуку гарантовано буде O(log_m n), де m — порядок дерева, як я зазначав вище.
❓ "Перебудова індексу", або чому вставки такі дорогі?
🔥 Основна причина популярності цих індексів - оптимізація роботи з диском. Диск читається блоками (вони ж сторінки індексу фіксованого розміру, зазвичай 4KB, 8KB або 16KB залежно від БД), і B-дерева чудово це використовують, зберігаючи багато ключів в одному вузлі.
Fill Factor (фактор заповнення) - параметр, який визначає, наскільки заповненими будуть сторінки індексу при їх створенні. Наприклад, fill factor 70% означає, що сторінки заповнюються на 70%, залишаючи 30% вільного місця для майбутніх вставок.
Коли сторінка індексу заповнюється повністю (через вставки даних), відбувається операція розщеплення сторінки (page split):
1️⃣ Створюється нова сторінка
2️⃣ Приблизно половина даних переміщується в нову сторінку
3️⃣ Середній ключ "піднімається" до батьківської сторінки
4️⃣ Батьківська сторінка оновлює свої посилання
❗️Це і є супер дорога операція, бо вимагає виділення нової сторінки на диску, переміщення даних, оновлення посилань, можливе розщеплення батьківських сторінок (каскадний ефект).
Також налаштування fill factor може підвищити продуктивність бази даних:
Для таблиць з частими вставками — низький fill factor (70-80%)
Для таблиць тільки для читання — високий fill factor (90-100%)
# Postgres
CREATE TABLE my_table (
id serial PRIMARY KEY,
data text
) WITH (fillfactor = 70);
# Для InnoDB доступний параметр fillfactor на рівні сервера
# в my.cnf або my.ini
[mysqld]
innodb_fill_factor = 80
👍 Сподіваюсь, у світі стало трошки менше магії, і трошки більше розуміння того, як працюють системи, якими ми користуємось щодня.
#database #middle
👍38🔥9⚡2🗿1
Ювілейний, 10-й випуск FWDays PHP Talks — вже на каналі
Ми нарешті сіли і поговорили про те, як не втопитися в логах, як збирати трейси в event-driven, як жити, коли в тебе триста мікросервісів.
Згадали такі речі, як EFK, Grafana Loki, Jaeger, Prometheus, OpenTelemetry, PageDuty, Graylog, "і отой забутий пайплайн на івентах, де ніхто не знає, що з чим з’єднане".
📍 Чим обсервабіліті відрізняється від моніторингу?
📍 Чому "все логуватити" — це погано, а не логувати — ще гірше?
📍 Скільки вартує моніторинг і чи треба він бізнесу?
📍 Чому Event-Driven це чудово, поки не треба дебажити?
👉 Подяка Йожефу, як завжди, за настрій і формат.
👍 Олексію — за архітектурну мудрість і чесність про "комфортну зону" архітектора.
🎧 Слухайте. Лайкайте. Пишіть нам, якщо з чимось не згодні 😉
https://youtu.be/frJGs9WovWY
#php #observability #monitoring #logging #tracing
Ми нарешті сіли і поговорили про те, як не втопитися в логах, як збирати трейси в event-driven, як жити, коли в тебе триста мікросервісів.
Згадали такі речі, як EFK, Grafana Loki, Jaeger, Prometheus, OpenTelemetry, PageDuty, Graylog, "і отой забутий пайплайн на івентах, де ніхто не знає, що з чим з’єднане".
📍 Чим обсервабіліті відрізняється від моніторингу?
📍 Чому "все логуватити" — це погано, а не логувати — ще гірше?
📍 Скільки вартує моніторинг і чи треба він бізнесу?
📍 Чому Event-Driven це чудово, поки не треба дебажити?
👉 Подяка Йожефу, як завжди, за настрій і формат.
👍 Олексію — за архітектурну мудрість і чесність про "комфортну зону" архітектора.
🎧 Слухайте. Лайкайте. Пишіть нам, якщо з чимось не згодні 😉
https://youtu.be/frJGs9WovWY
#php #observability #monitoring #logging #tracing
🔥14👍6
PHP 8.5: Clone with - майже те, що треба
Готувався до подкасту, де будемо розглядати нові можливості PHP 8.5 і неочікувано натрапив те, що цю фічу вже імплементували. Я був шокований, адже я чекав цього ще з моменту, як ввели readonly property (php8.1), навіть був RFC, котрий заглох, а тут бах, і вже змержили. Що правда не все супер райдужно, адже фактично створили новий RFC і імплементацію розділили на кілька етапів. То давайте розбиратись
❓ Чим круті readonly properties та іммутабельні обʼєкти
► Не треба гратись з інкапсуляцією, створювати додаткові getters/setters
► Ніхто випадково не поміняє дані десь в глибині коду
► Можна безпечно передавати об'єкт кудись - він точно повернеться таким самим
► Легше дебажити - об'єкт не може магічно змінитись між рядками коду
► Thread safety з коробки (якщо раптом дійде до async в PHP 9)
Але все це круто до моменту поки ми не захочемо щось з цим обʼєктом таки зробити [pic | code]
І через це пилили костилі, приходилось або писати купу окремих with методів [pic | code] (просто копіпасту), або гратись з рефлексією, що також є ресурсозатратно і не зовсім надійно [pic | code]
І тепер в PHP 8.5 ми зможемо досягти результату дуже просто
Ну майже просто 😅
🗿 Які є підводні камені?
Справа в тому, що ми не можемо просто взяти і зробити це без додаткових дій, адже коли в PHP 8.4 формалізували asymmetric visibility (можна окремо задавати видимість для читання і запису), стало зрозуміло, що всі public readonly властивості по дефолту були public(get) protected(set).
Чому protected(set) а не public(set) за замовчуванням, бо readonly властивості з самого початку (PHP 8.1) були задумані як "встановлюються тільки в конструкторі". Ну і не private(set), щоб можна було цим керувати в дочірніх класах
👉 Саме тому, при спробі викликати клонування в 8.5 поза обʼєктом призведе до відповідної помилки [pic | code], тому треба про це памʼятати і якщо необхідно - явно вказувати public(set) для властивостей вашого обʼєкту [pic | code]
💡 Або, робити відповідні зміни прямо всередині вашого обʼєкта, додаючи відповідні методи [pic | code], що мені подобається більше, але підійде не для всіх випадків.
🤔 Чи по тій самій схемі, замінити страшний приклад з рефлексією на реалізацію простого with [pic | code]
Ще один важливий момент
Поточна реалізація ніяк не впливає на використання магічного метода __clone, тобто параметри в нього досі передавати не можна, а при його імплементації ми отримаємо копію обʼєкту БЕЗ урахування того, що ми передали в якості аргументу [pic | code]. Це було свідоме рішення під час розбиття rfc на різні етапи, бо ця фіча ломає BC і сильно ускладнює імплементацію
💪 І тим не менш, я думаю, що Clone with - це крок в правильному напрямку
Поки памʼятаємо про protected(set) для readonly, скоріш за все це зміниться, та __clone() без параметрів, котрі обіцяють додати в майбутньому.
Але навіть з усіма нюансами - це краще ніж те, чим ми користувались. А ви що думаєте? Будете використовувати clone with?
#php85 #readonly #clonewith #middle #source
Готувався до подкасту, де будемо розглядати нові можливості PHP 8.5 і неочікувано натрапив те, що цю фічу вже імплементували. Я був шокований, адже я чекав цього ще з моменту, як ввели readonly property (php8.1), навіть був RFC, котрий заглох, а тут бах, і вже змержили. Що правда не все супер райдужно, адже фактично створили новий RFC і імплементацію розділили на кілька етапів. То давайте розбиратись
❓ Чим круті readonly properties та іммутабельні обʼєкти
► Не треба гратись з інкапсуляцією, створювати додаткові getters/setters
► Ніхто випадково не поміняє дані десь в глибині коду
► Можна безпечно передавати об'єкт кудись - він точно повернеться таким самим
► Легше дебажити - об'єкт не може магічно змінитись між рядками коду
► Thread safety з коробки (якщо раптом дійде до async в PHP 9)
Але все це круто до моменту поки ми не захочемо щось з цим обʼєктом таки зробити [pic | code]
І через це пилили костилі, приходилось або писати купу окремих with методів [pic | code] (просто копіпасту), або гратись з рефлексією, що також є ресурсозатратно і не зовсім надійно [pic | code]
І тепер в PHP 8.5 ми зможемо досягти результату дуже просто
$mariaViolin = clone($maria, ['instrument' => 'скрипка']);
Ну майже просто 😅
🗿 Які є підводні камені?
Справа в тому, що ми не можемо просто взяти і зробити це без додаткових дій, адже коли в PHP 8.4 формалізували asymmetric visibility (можна окремо задавати видимість для читання і запису), стало зрозуміло, що всі public readonly властивості по дефолту були public(get) protected(set).
Чому protected(set) а не public(set) за замовчуванням, бо readonly властивості з самого початку (PHP 8.1) були задумані як "встановлюються тільки в конструкторі". Ну і не private(set), щоб можна було цим керувати в дочірніх класах
👉 Саме тому, при спробі викликати клонування в 8.5 поза обʼєктом призведе до відповідної помилки [pic | code], тому треба про це памʼятати і якщо необхідно - явно вказувати public(set) для властивостей вашого обʼєкту [pic | code]
💡 Або, робити відповідні зміни прямо всередині вашого обʼєкта, додаючи відповідні методи [pic | code], що мені подобається більше, але підійде не для всіх випадків.
🤔 Чи по тій самій схемі, замінити страшний приклад з рефлексією на реалізацію простого with [pic | code]
Ще один важливий момент
Поточна реалізація ніяк не впливає на використання магічного метода __clone, тобто параметри в нього досі передавати не можна, а при його імплементації ми отримаємо копію обʼєкту БЕЗ урахування того, що ми передали в якості аргументу [pic | code]. Це було свідоме рішення під час розбиття rfc на різні етапи, бо ця фіча ломає BC і сильно ускладнює імплементацію
💪 І тим не менш, я думаю, що Clone with - це крок в правильному напрямку
Поки памʼятаємо про protected(set) для readonly, скоріш за все це зміниться, та __clone() без параметрів, котрі обіцяють додати в майбутньому.
Але навіть з усіма нюансами - це краще ніж те, чим ми користувались. А ви що думаєте? Будете використовувати clone with?
#php85 #readonly #clonewith #middle #source
🔥25👍18
Media is too big
VIEW IN TELEGRAM
Один із трендових напрямків зараз — інтеграція AI у CI/CD 🤖🚀
І це реально круто, що на етапі CI воно може в рази пришвидшити написання та перевірку коду.
Але коли мова про CD, тут ще є нюанси 🙃
Моделі статистичні → значить, вони недетерміновані. Це може давати:
❌ фолс-позитивні результати (бачать проблему там, де її нема)
🤷♂️ пропускати важливі речі (не ловлять баг, коли він є)
🌀 «галюцинації»
🙅♂️ «огріхи» від неповного датасету чи неідеального навчання
Попри все, тренд рухається вперед. І цікаво спостерігати, як AI буде поступово «вростати» у CI/CD процеси 💡
І це реально круто, що на етапі CI воно може в рази пришвидшити написання та перевірку коду.
Але коли мова про CD, тут ще є нюанси 🙃
Моделі статистичні → значить, вони недетерміновані. Це може давати:
❌ фолс-позитивні результати (бачать проблему там, де її нема)
🤷♂️ пропускати важливі речі (не ловлять баг, коли він є)
🌀 «галюцинації»
🙅♂️ «огріхи» від неповного датасету чи неідеального навчання
Попри все, тренд рухається вперед. І цікаво спостерігати, як AI буде поступово «вростати» у CI/CD процеси 💡
👍16🙊3🔥1
Все, що потрібно знати про PHP 8.5: від clone with до Fatal Errors stack trace
Друзі, цього разу ми з Йожефом пройшлися по прийнятих RFC у PHP 8.5 — від клонування імутабельних об’єктів до покращення OPcache і свіжого pipe-оператора.
📍Що розібрали по суті (з живими прикладами):
• Immutability + clone with — нормальний шлях для value objects/DTO. Порівняли з попередніми «костилями» (рефлексія, копіпасти with*), розібрали нюанси: публічний set для readonly, порядок викликів та відсутність параметрів у __clone, shallow vs deep copy.
• URI-класи в ядрі (RFC 3986 / WHATWG) — стандартизований парсинг без власноручних регексів.
• array_first() / array_last() — прозорий доступ до крайніх елементів без reset()/end() та внутрішніх поінтерів; чому це справді краще за array_key_first()/array_key_last().
• Pipe operator |> — ланцюжки викликів у зрозумілому функціональному стилі.
• #[NoDiscard] — ловимо тих, зто «забув використати» дані з return.
• Стек-трейс у Fatal Errors — нарешті нормальний дебаг замість «білого екрану».
👉Велика подяка Йожефу за настрій та практичні поради, як завжди було круто
Випуск вже на каналі. Слухайте, дивіться, ставте питання та вподобайку, якщо було корисно.
https://youtu.be/abHntd9evic?si=BGg-K9KagHf1dXkF
Друзі, цього разу ми з Йожефом пройшлися по прийнятих RFC у PHP 8.5 — від клонування імутабельних об’єктів до покращення OPcache і свіжого pipe-оператора.
📍Що розібрали по суті (з живими прикладами):
• Immutability + clone with — нормальний шлях для value objects/DTO. Порівняли з попередніми «костилями» (рефлексія, копіпасти with*), розібрали нюанси: публічний set для readonly, порядок викликів та відсутність параметрів у __clone, shallow vs deep copy.
• URI-класи в ядрі (RFC 3986 / WHATWG) — стандартизований парсинг без власноручних регексів.
• array_first() / array_last() — прозорий доступ до крайніх елементів без reset()/end() та внутрішніх поінтерів; чому це справді краще за array_key_first()/array_key_last().
• Pipe operator |> — ланцюжки викликів у зрозумілому функціональному стилі.
• #[NoDiscard] — ловимо тих, зто «забув використати» дані з return.
• Стек-трейс у Fatal Errors — нарешті нормальний дебаг замість «білого екрану».
👉Велика подяка Йожефу за настрій та практичні поради, як завжди було круто
Випуск вже на каналі. Слухайте, дивіться, ставте питання та вподобайку, якщо було корисно.
https://youtu.be/abHntd9evic?si=BGg-K9KagHf1dXkF
YouTube
RFC-тур по PHP 8.5: Pipe Operator, Clone with, #[NoDiscard], stack trace та інші
Зустрічайте чотирнадцятий випуск Fwdays PHP Talks!
У цьому випуску наші постійні спікери — Йожеф Гісем і Кирило Сулімовський — обговорять нові RFC та можливості PHP 8.5, які вплинуть на щоденний код.
На що варто підписатися:
– Більше цікавого для розробників:…
У цьому випуску наші постійні спікери — Йожеф Гісем і Кирило Сулімовський — обговорять нові RFC та можливості PHP 8.5, які вплинуть на щоденний код.
На що варто підписатися:
– Більше цікавого для розробників:…
👍34🔥6⚡1
⚡ Ну що, нарешті PHP fwdays’25
Особисто для мене це завжди крута подія, котру кожен рік чекаю, щоб засинхронізуватись з колегами, подивитись що відбувається у інших, заглибитись в актуальні підходи, поговорити про архітектуру та використання фреймворків, дізнатись кейси по інтеграції AI у PHP і т.д.
Коли: 4 жовтня (субота)
Де: Київ (офлайн, лише 80 місць) + онлайн
Серед спікерів:
— Андрій Яценко, Oro Inc. — «Застрибуємо у гайп-потяг AI розробки, поки він не згорів»
— Владислав Поздняков, mono — «Long running processes на PHP»
— Юрій Панайотов, Silpo (E-commerce) — «Як мати 200+ PHP сервісів у продакшні і не з’їхати з глузду?»
— В’ячеслав Вергелес, TENTENS tech — «Міцний сон інженера: як ми забезпечуємо якість, щоб спати спокійно»
— Олександр Бучек, MacPaw — «Не клонувати інфру, а ізолювати дані: ефемерні PR-оточення у shared-env»
🎟 Квитки вже у продажу. Діє знижка 10% за моїм промокодом PHP10Beer.
🔗 Деталі та квитки
Особисто для мене це завжди крута подія, котру кожен рік чекаю, щоб засинхронізуватись з колегами, подивитись що відбувається у інших, заглибитись в актуальні підходи, поговорити про архітектуру та використання фреймворків, дізнатись кейси по інтеграції AI у PHP і т.д.
Коли: 4 жовтня (субота)
Де: Київ (офлайн, лише 80 місць) + онлайн
Серед спікерів:
— Андрій Яценко, Oro Inc. — «Застрибуємо у гайп-потяг AI розробки, поки він не згорів»
— Владислав Поздняков, mono — «Long running processes на PHP»
— Юрій Панайотов, Silpo (E-commerce) — «Як мати 200+ PHP сервісів у продакшні і не з’їхати з глузду?»
— В’ячеслав Вергелес, TENTENS tech — «Міцний сон інженера: як ми забезпечуємо якість, щоб спати спокійно»
— Олександр Бучек, MacPaw — «Не клонувати інфру, а ізолювати дані: ефемерні PR-оточення у shared-env»
🎟 Квитки вже у продажу. Діє знижка 10% за моїм промокодом PHP10Beer.
🔗 Деталі та квитки
🔥15👍6
Сьогодні важлива подія
Думаю ні для кого не секрет, що насправді я давненько відійшов від постійного програмування на PHP, займаю менеджерську позицію, пробую себе в golang, python та іншому стеку.
🤷 Виходить так, що контекст змінився, а канал ні. Мені хочеться ділитись думками частіше, та завжди себе зупиняю, що вони "не в тему каналу".
👉 Важлива подія в тому, що тільки но закатив перший відос на свій YouTube канал. Він зовсім не про PHP, він про те як якісно проводити 1-on-1. Про подібні речі також хочеться писати пости, отримувати фідбек, ділитись спостереженнями і дискутувати.
Тому:
1. Закликаю всіх подивитись відос, буду вдячний за коментарі, критику, особливо конструктивну
2. Дати реакцій, чи заходить така тематика і подібний контент
3. Кому заходить - підтримати підпискою, вподобайкою, зворотнім звʼязком
https://youtu.be/s2wuc-t3ZO0
#1-1 #manager #teamlead #youtube
Думаю ні для кого не секрет, що насправді я давненько відійшов від постійного програмування на PHP, займаю менеджерську позицію, пробую себе в golang, python та іншому стеку.
🤷 Виходить так, що контекст змінився, а канал ні. Мені хочеться ділитись думками частіше, та завжди себе зупиняю, що вони "не в тему каналу".
👉 Важлива подія в тому, що тільки но закатив перший відос на свій YouTube канал. Він зовсім не про PHP, він про те як якісно проводити 1-on-1. Про подібні речі також хочеться писати пости, отримувати фідбек, ділитись спостереженнями і дискутувати.
Тому:
1. Закликаю всіх подивитись відос, буду вдячний за коментарі, критику, особливо конструктивну
2. Дати реакцій, чи заходить така тематика і подібний контент
3. Кому заходить - підтримати підпискою, вподобайкою, зворотнім звʼязком
https://youtu.be/s2wuc-t3ZO0
#1-1 #manager #teamlead #youtube
YouTube
Структура ідеального 1:1: покроковий алгоритм для менеджерів
У цьому відео ділюся покроковим планом ефективних 1-on-1 зустрічей - з чіткою структурою та практичними інструментами, які допоможуть вибудувати довіру, знизити ризик вигорання й підвищити продуктивність команди.
00:00 Вступ
00:44 Мета та значення 1-on…
00:00 Вступ
00:44 Мета та значення 1-on…
👍31🔥13
Нарешті це сталось. Я повністю перебрав, доповнив і опублікував статтю на DOU — «Принцип підстановки Барбари Лісков (про передумови, постумови та інваріанти)».
Спробував максимально розжувати те, що ж Барбара мала на увазі, додати конкретики та пройтися по кожному важливому пункту 🙂
У статті розповів про:
• принцип самої підстановки
• передумови та післяумови
• коваріантність і контраваріантність
• як з цим жити або як обійти
📌 Якщо ви вважали цей принцип незрозумілим — рекомендую до прочитання, адже я постарався навести реальні приклади коду та чіткі пояснення.
🙃 До речі, давно не писав у такому форматі, тож радий повернутися.
👇🏻 Діліться своїм досвідом у коментарях — цікаво почути, чи стало трошки зрозуміліше, що воно таке і з чим його їдять.
https://dou.ua/forums/topic/55754/
Спробував максимально розжувати те, що ж Барбара мала на увазі, додати конкретики та пройтися по кожному важливому пункту 🙂
У статті розповів про:
• принцип самої підстановки
• передумови та післяумови
• коваріантність і контраваріантність
• як з цим жити або як обійти
📌 Якщо ви вважали цей принцип незрозумілим — рекомендую до прочитання, адже я постарався навести реальні приклади коду та чіткі пояснення.
🙃 До речі, давно не писав у такому форматі, тож радий повернутися.
👇🏻 Діліться своїм досвідом у коментарях — цікаво почути, чи стало трошки зрозуміліше, що воно таке і з чим його їдять.
https://dou.ua/forums/topic/55754/
DOU
Принцип підстановки Барбари Лісков. Про передумови, постумови та інваріанти
У статті автор розбирає принцип Лісков на практичних прикладах, як працювати з базовими та спадковими класами, щоб уникнути помилок. А також пояснює передумови, постумови, інваріанти і «правило історії» через концепцію «Design by Contract».
🔥52👍7
Друзі, продовжую низку неочікуваних новин 🙂 Давно хотілось створити подкаст, де говорити на менш технічні і на більш загальні теми в айтішці. Про професії, зони відповідальності, курйозні ситуації, ринок, проблеми, чи просто понити.
То ж вийшов перший пілотний випуск, в котрому спробували прояснити «Хто такий Delivery Manager»
Було дійсно цікаво поговорити з Олею, вона Delivery Manager із понад 10-річним досвідом у міжнародних компаніях.
📍 Якщо коротко, що затронули:
– чим насправді відрізняється Project, Product і Delivery Manager;
– як менеджери справляються зі стресом і факапами;
– чи потрібна технічна експертиза, щоб ефективно керувати командою;
– як виживає команда без менеджера та що не так із Jira;
– чи може штучний інтелект замінити менеджера.
🎥 Випуск уже на каналі
Переходьте, дивіться, ставте запитання в коментарях і буду дуже вдячний за ваш зворотній звʼязок
https://youtu.be/RGyYU2fhDtY
То ж вийшов перший пілотний випуск, в котрому спробували прояснити «Хто такий Delivery Manager»
Було дійсно цікаво поговорити з Олею, вона Delivery Manager із понад 10-річним досвідом у міжнародних компаніях.
📍 Якщо коротко, що затронули:
– чим насправді відрізняється Project, Product і Delivery Manager;
– як менеджери справляються зі стресом і факапами;
– чи потрібна технічна експертиза, щоб ефективно керувати командою;
– як виживає команда без менеджера та що не так із Jira;
– чи може штучний інтелект замінити менеджера.
🎥 Випуск уже на каналі
Переходьте, дивіться, ставте запитання в коментарях і буду дуже вдячний за ваш зворотній звʼязок
https://youtu.be/RGyYU2fhDtY
YouTube
Хто такий Delivery Manager? Про роботу, факапи і стрес
Сьогодні ми говоримо відверто про IT! 🔥
Думаєте, менеджер в IT — це просто "передавати таски" і "точити ляси"? Ви глибоко помиляєтесь. У цьому інтерв'ю ми розкриваємо реалії однієї з найвідповідальніших і найстресовіших ролей в індустрії.
Наша гостя — Оля…
Думаєте, менеджер в IT — це просто "передавати таски" і "точити ляси"? Ви глибоко помиляєтесь. У цьому інтерв'ю ми розкриваємо реалії однієї з найвідповідальніших і найстресовіших ролей в індустрії.
Наша гостя — Оля…
🔥17👍7⚡6
Записали черговий подкаст для FWDays PHP Talks з Павлом і Йожефом. Цього разу копнули Event Driven архітектуру.
Почали з основ — навіщо це все придумали, а потім розібрали три стовпи Event Driven: Event, Producer та Subscriber.
📍Ну і, звісно, поговорили про вузькі місця:
• Хто створює черги — продюсер чи сабскрайбер?
• Як правильно налаштовувати конфіги?
• Що робити з навантаженням і скейлінгом воркерів?
• І найболючіше — коли падають воркери, у тебе починає деградувати вся система.
Плюс затримки. Ті самі затримки, від яких нікуди не подітись 😅
👉 Коротше, якщо працюєте з Event Driven або тільки плануєте впроваджувати — рекомендую послухати. Ми розповіли про все те, з чим самі стикалися і що, на жаль, доводилось розгрібати.
Переходьте, дивіться, пишіть вашу думку і фідбек — буду радий подискутувати 🙂
https://youtu.be/SNplV4BaKvU
Почали з основ — навіщо це все придумали, а потім розібрали три стовпи Event Driven: Event, Producer та Subscriber.
📍Ну і, звісно, поговорили про вузькі місця:
• Хто створює черги — продюсер чи сабскрайбер?
• Як правильно налаштовувати конфіги?
• Що робити з навантаженням і скейлінгом воркерів?
• І найболючіше — коли падають воркери, у тебе починає деградувати вся система.
Плюс затримки. Ті самі затримки, від яких нікуди не подітись 😅
👉 Коротше, якщо працюєте з Event Driven або тільки плануєте впроваджувати — рекомендую послухати. Ми розповіли про все те, з чим самі стикалися і що, на жаль, доводилось розгрібати.
Переходьте, дивіться, пишіть вашу думку і фідбек — буду радий подискутувати 🙂
https://youtu.be/SNplV4BaKvU
YouTube
Event-Driven Architecture: магія чи хаос під капотом?
Зустрічайте 15 випуск Fwdays PHP Talks!
У цьому випуску наші постійні спікери — Йожеф Гісем і Кирило Сулімовський, разом із гостем Павлом Акіменко, обговорюють Event-Driven Architecture:
- звідки походить цей підхід і як він змінив сучасний бекенд
- коли…
У цьому випуску наші постійні спікери — Йожеф Гісем і Кирило Сулімовський, разом із гостем Павлом Акіменко, обговорюють Event-Driven Architecture:
- звідки походить цей підхід і як він змінив сучасний бекенд
- коли…
🔥23👍3
Чому я почав вести YouTube? 🎬
А чи має бути причина?
Ніби без причини щось не має сенсу. Чому, коли хочеться спробувати щось нове, ти одразу шукаєш, пояснення, мотивацію, а іноді і виправдання?
Не все має починатись з логіки. Деякі речі народжуються просто тому, що хочеться, тому, що є внутрішній імпульс — і все, без уточнення «навіщо».
Мені подобається говорити, спілкуватись, ділитись думками, записувати подкасти, знайомитись з людьми з індустрії, отримувати фідбек, як позитивний, так і той, після якого хочеться рости.
І цього достатньо.
Не шукайте «чому». Коли всередині є справжнє бажання — це вже і є причина
https://youtube.com/@beercodeit?si=ZHLpe7xaN3eGQfqo
А чи має бути причина?
Ніби без причини щось не має сенсу. Чому, коли хочеться спробувати щось нове, ти одразу шукаєш, пояснення, мотивацію, а іноді і виправдання?
Не все має починатись з логіки. Деякі речі народжуються просто тому, що хочеться, тому, що є внутрішній імпульс — і все, без уточнення «навіщо».
Мені подобається говорити, спілкуватись, ділитись думками, записувати подкасти, знайомитись з людьми з індустрії, отримувати фідбек, як позитивний, так і той, після якого хочеться рости.
І цього достатньо.
Не шукайте «чому». Коли всередині є справжнє бажання — це вже і є причина
https://youtube.com/@beercodeit?si=ZHLpe7xaN3eGQfqo
👍16🔥6
Друзі, маю для вас новий випуск на своєму каналі 🚀
Продовжуємо говорити відверто про айтішку. Цього разу ми спробували розібратися, хто ж такий DevOps інженер і чому всі кажуть, що ця роль "не для джунів".
В гостях був Томош – Cloud DevOps інженер, який погодився простою мовою пояснити, за що йому платять гроші 😁
📍 Якщо коротко, про що поговорили:
– Чому насправді "не буває Junior DevOps" і звідки тоді вони беруться?
– SysAdmin, DevOps, DevSecOps, FinOps – у чому різниця і де чия відповідальність?
– "Лінивий DevOps" – це хороший DevOps?
– Що бісить девопсів найбільше?
– Наскільки глибоко DevOps має знати код (Python, GoLang) і чи важливі soft skills?
– Вплив ШІ: як Copilot, Grok та Claude вже змінюють підходи до інфраструктури та автоматизації
Переходьте, дивіться, і, звісно, діліться своїми історіями та запитаннями в коментарях. Буду дуже вдячний за ваш зворотній звʼязок!
https://youtu.be/s-TTevLwMPA
Продовжуємо говорити відверто про айтішку. Цього разу ми спробували розібратися, хто ж такий DevOps інженер і чому всі кажуть, що ця роль "не для джунів".
В гостях був Томош – Cloud DevOps інженер, який погодився простою мовою пояснити, за що йому платять гроші 😁
📍 Якщо коротко, про що поговорили:
– Чому насправді "не буває Junior DevOps" і звідки тоді вони беруться?
– SysAdmin, DevOps, DevSecOps, FinOps – у чому різниця і де чия відповідальність?
– "Лінивий DevOps" – це хороший DevOps?
– Що бісить девопсів найбільше?
– Наскільки глибоко DevOps має знати код (Python, GoLang) і чи важливі soft skills?
– Вплив ШІ: як Copilot, Grok та Claude вже змінюють підходи до інфраструктури та автоматизації
Переходьте, дивіться, і, звісно, діліться своїми історіями та запитаннями в коментарях. Буду дуже вдячний за ваш зворотній звʼязок!
https://youtu.be/s-TTevLwMPA
YouTube
Хто такий DevOps? Про щоденні задачі, провали та несподівані виклики
У цьому відео ми відверто спілкуємось про DevOps та роботу в IT з Томашем, досвідченим Cloud DevOps-інженером. Розбираємося, хто такий DevOps, чим він відрізняється від сісадміна, розробника й інших суміжних ролей, а також обговорюємо:
• Навіщо DevOps…
• Навіщо DevOps…
👍11🔥3🙊1
Друзі, вийшла друга частина розмови Fwdays PHP Talks про DDD! 🎥
Разом з Йожефом Гісемом та Ігорем Проніним продовжили говорити про те, як бізнес і розробка можуть нарешті порозумітися 🙂
У цьому епізоді:
💬 про те, як знайти спільну мову між бізнесом і девелоперами — щоб усі рухались в одному напрямку, а не говорили різними мовами
🧩 коли реально варто використовувати Event Storming і контекст-мапи — і як ці штуки допомагають розкласти складну систему по поличках
⚙️ Value Object, Entity та Rich Model — не просто теорія, а як це працює у живих проєктах
Якщо тема DDD тобі близька — обов’язково глянь випуск і поділися своїми спостереженнями в коментарях 👇🏻
https://youtu.be/AnicWaTTvLg?si=4PFnfL_SLmBRL6AM
Разом з Йожефом Гісемом та Ігорем Проніним продовжили говорити про те, як бізнес і розробка можуть нарешті порозумітися 🙂
У цьому епізоді:
💬 про те, як знайти спільну мову між бізнесом і девелоперами — щоб усі рухались в одному напрямку, а не говорили різними мовами
🧩 коли реально варто використовувати Event Storming і контекст-мапи — і як ці штуки допомагають розкласти складну систему по поличках
⚙️ Value Object, Entity та Rich Model — не просто теорія, а як це працює у живих проєктах
Якщо тема DDD тобі близька — обов’язково глянь випуск і поділися своїми спостереженнями в коментарях 👇🏻
https://youtu.be/AnicWaTTvLg?si=4PFnfL_SLmBRL6AM
YouTube
DDD: складно, але потрібно | Як говорити однією мовою з бізнесом і не зійти з розуму від контекстів
Зустрічайте шістнадцятий випуск Fwdays PHP Talks!
У цьому випуску наші постійні спікери — Йожеф Гісем і Кирило Сулімовський, разом із гостем Ігорем Проніним, продовжують розмову про Domain-Driven Design (DDD):
- Як бізнес і розробка знаходять спільну мову…
У цьому випуску наші постійні спікери — Йожеф Гісем і Кирило Сулімовський, разом із гостем Ігорем Проніним, продовжують розмову про Domain-Driven Design (DDD):
- Як бізнес і розробка знаходять спільну мову…
👍20🔥3
Звільнення
Мабуть, одна з найбільш табуйованих і неприємних тем у менеджменті.
Зізнавайтесь, чи бачили ви співробітника, якого "тягнуть"?
🤔 "А раптом він виправиться?", "Шкода людину", "Може, це я погано пояснив?".
Я проходив через ці сумніви. Але правда в тому, що коли ви не звільняєте неефективну людину, ви руйнуєте команду зсередини.
👉 Тому у новому відео вирішив підняти цю важку тему і розкласти все по поличках:
• Чому ми насправді боїмося звільняти (і до чого тут емпатія)
• Оцінка 360 та 9-box model: як зрозуміти, хто "тягне", а хто ні
• Токсичність, чому це бомба сповільненої дії
• Performance Improvement Plan (PIP)
• Покроковий скрипт, як повідомити про рішення, щоб це було професійно
Звільнення - це, мабуть, найскладніше рішення для менеджера. Але іноді - найправильніше
Тому дивіться відео, там багато практичних інсайтів: https://youtu.be/lZ-a_LzJXhU
Буду вдячний за зворотній звʼязок
#manager #teamlead #youtube
Мабуть, одна з найбільш табуйованих і неприємних тем у менеджменті.
Зізнавайтесь, чи бачили ви співробітника, якого "тягнуть"?
🤔 "А раптом він виправиться?", "Шкода людину", "Може, це я погано пояснив?".
Я проходив через ці сумніви. Але правда в тому, що коли ви не звільняєте неефективну людину, ви руйнуєте команду зсередини.
👉 Тому у новому відео вирішив підняти цю важку тему і розкласти все по поличках:
• Чому ми насправді боїмося звільняти (і до чого тут емпатія)
• Оцінка 360 та 9-box model: як зрозуміти, хто "тягне", а хто ні
• Токсичність, чому це бомба сповільненої дії
• Performance Improvement Plan (PIP)
• Покроковий скрипт, як повідомити про рішення, щоб це було професійно
Звільнення - це, мабуть, найскладніше рішення для менеджера. Але іноді - найправильніше
Тому дивіться відео, там багато практичних інсайтів: https://youtu.be/lZ-a_LzJXhU
Буду вдячний за зворотній звʼязок
#manager #teamlead #youtube
YouTube
Алгоритм звільнення: від попередження до прощальної розмови
У цьому відео розкриваю покроковий алгоритм звільнення — від подолання власного страху і збору аргументів до коректної розмови та комунікації з командою. На реальних прикладах показую, як вчасно виявити неефективність працівника, чим загрожує токсична поведінка…
🔥8👍4