Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

25.04.2023

Наступність схеми даних

Якщо у вас є дані, що зберігаються поза процесом програми, та переживають її перезапуск, то при зміні схеми цих даних необхідно подумати про наступність, тобто здатність нового коду читати не тільки нову, але й стару схему. Більш за те, треба також передбачити зворотне — тобто старий код має вміти читати нову схему. Бо інакше як ти зможеш відкотити додаток на попередню версію? Спалювати цей міст необачно — виправлення помилок без можливості відкату набуває особливої пікантності.

В реляційних базах даних з цим трохи простіше, та підходи зрілі (принаймні, в Ruby on Rails це так.) Але дані криються не тільки в базі. Наприклад, в будь-якій черзі теж треба передбачати наступність. Власне, це мене сьогодні й вкусило. Та ще й не на Ruby, а на Go. Тут все ускладнюється типізацією — бо якщо в Ruby можна сподіватись що так-сяк спрацює, то в Go несумісний тип не проходив через json.Unmarshal, та на цьому все завершувалось.

Щоб не мати проблем, потрібно спочатку в одній версій коду додавати поле, але тільки в наступній з ним щось робити. Так само з видаленням полів: спочатку припинити використання та зробити omitempty, а потім вже видаляти з типу. Особливо цікаво це коли треба поміняти тип поля, та я б цього взагалі не радив робити без перейменування (тобто фактично створення нового поля та видалення старого.)

Але якщо вже так сталося, що схема поламана, що робити тоді? Тоді “гарячим” виправленням треба ввести проміжний код, який буде підтримувати обидві схеми. Ну, наприклад, як саме просте рішення, можна пробувати розкодувати в новий тип, а якщо не вдасться — то в старий та адаптувати. Або розкодувати щось в інтерфейс та потім дивитись на тип. (До речі. цікавий момент: якщо в Go розкодовувати чисельне значення з JSON в interface{}, отримаєш тип float64, бо технічно в JSON немає різниці між цілими та дробними числами.)

Раджу обмежити виправлення вхідною частиною програми, а далі використати тільки чистий новий тип — як всередині бізнес-логіки, так і на запис. Це локалізує “нечіткий” код та уникне помилок в головній частині програми.