Kotlin: sealed interfaces и when-выражения — паттерн-матчинг как в Rust
Как sealed interfaces из Kotlin 1.5+ вместе с when-выражениями дают мощный паттерн-матчинг, похожий на Rust enum+match, и почему мы перешли на ADT для доменных событий.
Заметки о веб-разработке, архитектуре, оптимизации и инструментах. Пишу о том с чем сталкиваюсь в реальных проектах.
статей
категорий
минут на чтение
Сравниваю Micronaut и Spring Boot для Kotlin-микросервисов: стартовое время, потребление памяти, холодный старт и реальный опыт миграции одного из сервисов.
Читать статьюКак sealed interfaces из Kotlin 1.5+ вместе с when-выражениями дают мощный паттерн-матчинг, похожий на Rust enum+match, и почему мы перешли на ADT для доменных событий.
Разбираюсь с накладными расходами JSON-сериализации при высоких нагрузках и показываю, как интегрировать Protobuf с kotlinx-serialization в микросервисной архитектуре на Kotlin.
Реальный опыт использования gRPC streaming в продакшене на Kotlin: server-side, client-side и bidirectional стриминг для системы отслеживания статусов заказов в реальном времени.
Нужна была real-time уведомления для дашборда. Выбор: SSE или WebSockets? SSE проще (односторонний, HTTP, автопереподключение), WebSockets мощнее (двусторонний). Для уведомлений хватило SSE, но для чата понадобились WebSockets. Рассказываю, как реализовать оба в Ktor и в каких случаях что выбирать.
Выбор инструмента миграций — кажется мелочью, пока не нужно откатить изменение в продакшене. Flyway — простой SQL-first подход. Liquibase — XML/YAML/JSON с абстракциями и rollback. Мы использовали оба в разных проектах и набили шишки на каждом. Рассказываю, в каком случае что выбрать и как не потерять данные при rollback.
HikariCP — пул подключений по умолчанию в Spring Boot и Ktor. На первый взгляд — configure and forget. На практике — неправильный размер пула убивает производительность, leak detection срабатывает с ложными позитивами, а connection timeout в Kubernetes приводит к каскадным сбоям. Рассказываю, как мы настраивали HikariCP для продакшена и что пошло не так.
Event Sourcing звучит элегантно: храним не текущее состояние, а все события, которые к нему привели. На практике — это другая архитектура мышления, другая модель данных и куча новых проблем: eventual consistency, snapshotting, replay событий. Рассказываю, как мы внедрили Event Sourcing в Kotlin-микросервисе с Kafka и чему научились на ошибках.
У нас был REST API с 30 endpoint'ами. Фронтенд постоянно просил добавить поля, убрать лишнее, объединить запросы. Перевели один микросервис на GraphQL (graphql-kotlin) и получили: фронтенд запрашивает только нужные данные, один запрос вместо 5-7, меньше нагрузки на сеть. Но появились свои проблемы: N+1 в резолверах, сложность кэширования, versioning.
В микросервисной архитектуре с 12 сервисами запрос проходит через 4-5 сервисов. Когда что-то тормозит — непонятно где. Добавили OpenTelemetry: теперь каждый запрос имеет trace ID, и мы видим полную цепочку вызовов с таймингами. Рассказываю, как настроить OpenTelemetry в Kotlin-микросервисе на Ktor и Spring Boot без боли.
Try-catch по всему коду — это антипаттерн, но альтернативы кажутся сложными. Arrow.kt предлагает Either, Raise, и ValidationError для типобезопасной обработки ошибок. Перешли на функциональный подход в микросервисе с 40+ endpoint'ами и код стал предсказуемым. Показываю реальный до/после.
Время сборки Kotlin-проекта выросло с 30 секунд до 4 минут. Причина — kapt и annotation processing. KSP (Kotlin Symbol Processing) призван это исправить, но миграция не тривиальна. Рассказываю, как мы ускорили сборку в 5 раз, заменив kapt на KSP в реальном проекте с Room, Moshi и Dagger.
JUnit 5 — стандарт де-факто, но Kotest предлагает Kotlin-native подход: data-driven тесты, поведение через BehaviorSpec, встроенные assertions. Перешли на Kotest в проекте со 1800 тестами и не пожалели. Но есть подводные камни, о которых молчат туториалы.
При росте микросервисной архитектуры встал вопрос: Kafka или RabbitMQ? Оба брокера используются в Kotlin-экосистеме, но выбор между ними — это не про «какой лучше», а про «какая проблема у вас». Рассказываю на опыте реального проекта, где мы переписали messaging-слой с RabbitMQ на Kafka и что из этого вышло.
Dependency injection — фундамент архитектуры. Koin — лёгкий, на чистом Kotlin, без кодогенерации. Dagger/Hilt — compile-time, от Google, промышленный стандарт. Сравниваю скорость компиляции, runtime overhead, DX и реальный опыт миграции с одного на другой на проекте с 150 зависимостями.
REST — привычный, универсальный, работает через JSON. gRPC — бинарный протокол, контракты через Protobuf, стриминг из коробки. На реальном проекте с 12 микросервисами сравниваю latency, размер полезной нагрузки, DX и почему выбор протокола зависит не только от производительности.
Мокать PostgreSQL в тестах — путь к ложной уверенности. Testcontainers поднимает настоящие контейнеры с БД, Redis, Kafka — и убивает после теста. В статье разбираю настройку Testcontainers с Ktor, Exposed, Flyway, совместное использование тестовых баз данных и ускорение тестов в CI/CD.
Redis — не просто key-value хранилище. Pub/Sub, Streams, Sorted Sets, Lua-скрипты — Redis позволяет реализовать сложные паттерны кэширования и event sourcing. Разбираю Cache-Aside, Write-Through, Write-Behind, паттерн Cache Stampede и реальный опыт оптимизации задержки с 80 мс до 5 мс.
Логирование — первая линия расследования при P0-инцидентах. Но в Kotlin есть нюансы: string interpolation, ленивые вычисления, структурированное логирование, correlation ID. Сравниваю klogging, SLF4J + Logback, Kotlin Logging и объясняю, почему формат логов важнее библиотеки.
Hibernate/JPA — не единственный вариант. В Kotlin-мире есть Exposed от JetBrains и jOOQ от Data Geekery. Обе библиотеки дают типобезопасный SQL без ORM-магии. Сравниваю DX, производительность, миграции и реальный опыт на проекте с 80 таблицами.
Корутины и Reactor — две парадигмы асинхронного программирования. Корутины выглядят как синхронный код, Reactor — как цепочка операторов. В статье разбираю когда какая модель выигрывает, как они соотносятся по производительности и почему выбор фреймворка уже определил ваш стек.
Два фреймворка, два мира. Spring Boot — монолит экосистемы с тысячей стартёров и аннотаций. Ktor — лёгкий, корутин-нативный фреймворк от JetBrains. Сравниваю DX, производительность, время старта и реальный опыт миграции с одного на другой.
Когда я впервые увидел слово suspend перед функцией в Kotlin, я был уверен, что это какая-то магия компилятора. Оказалось, что за этим стоит принципиально иной подход к асинхронности. В этой статье разбираю, как работают корутины под капотом, зачем нужен Continuation, и почему State Machine — это не страшный монстр, а ваш друг.
Обе библиотеки решают одну задачу — превращают JSON в объекты и обратно. Но разница в подходах колоссальная. Moshi использует рефлексию в рантайме, kotlinx — компиляторный плагин. В статье сравниваю производительность, DX и грабли каждой библиотеки на реальном проекте.
Flow — это реактивные потоки Kotlin, но с корутин-нативным подходом. В статье разбираю горячие и холодные потоки, SharedFlow, StateFlow, backpressure, error handling и реальный опыт использования Flow в production на нагрузке 50K rps.
Я занимаюсь Android-разработкой, и через мои руки прошло немало приложений, где рано или поздно вставал вопрос: а не завезти ли нам виджет? Пользователи просили, продакты настаивали, метрики показывали, что конкуренты с виджетами имеют Retention на 20-30% выше. И каждый раз я откладывал эту задачу в бэклог с пометкой «когда-нибудь потом».