Олег про Java
61 subscribers
12 links
Рассказываю о работе Java/Kotlin Backend разработчиком. Помогаю устроиться на первую работу. Делюсь опытом

Если нужна консультация, то пишите в лс @stepoleggg, это бесплатно
Download Telegram
Channel created
Channel photo updated
От геймера до Java-разработчика: моя IT история

Привет, я Олег! 👋
Уже 5 лет я работаю backend-разработчиком на Java и Kotlin, последние 3 года — в fintech. Но начиналось всё совсем не с этого...

🎮 Начало пути: от PlayStation до создания игр


Всё детство я провёл за PlayStation 1, зачитываясь журналом "Игромания". Когда появился компьютер, друг показал мне движок-конструктор игр. Это было откровение! С помощью блок-схем я мог создавать собственные 2D игры. За пару лет я сделал десятки игр и микро-демок. Я был в восторге от возможности воплощать свои идеи в виртуальном мире.

💻 Открытие web-разработки


Однажды на папиной полке я нашел книгу по HTML 4. Она захватила меня целиком! Оказалось, что красивые интерактивные проекты можно делать прямо в браузере, используя только блокнот. Весна того года пролетела за изучением HTML, CSS, JavaScript и PHP. Я нашел много уроков на YouTube и понял, что уже давно вышел HTML 5 :)
Тогда я не придумал ничего лучше, чем создать свою социальную сеть. Друг помог оформить бесплатный домен и захостить код на сервере. Это было волшебное время открытий и бессонных ночей за кодом до 4 утра. Тогда я чувствовал, что могу сделать все что угодно

💼 Первые деньги

Программирование оставалось хобби (моя социальная сеть не взлетела, ахах), пока однокурсник в универе не рассказал о фрилансе. Я решил попробовать и начал брать любую работу в сфере IT. Вскоре заметил большой спрос на телеграм-ботов и сфокусировался на этом. За год работы я смог купить ноутбук и съездить в Южную Корею!
Но со временем я понял ограничения фриланса: нестабильный доход, отсутствие командной работы и размытые границы рабочего времени.

🚀 Переход в "большое" IT

На 4 курсе я решил устроиться в настоящую IT-компанию. Создал резюме, указав фриланс как опыт, и начал откликаться на вакансии в Екатеринбурге.
Первое собеседование по Java я с треском провалил. Вопросы про LinkedList и алгоритмическую сложность застали меня врасплох. Но второе собеседование прошло легко — я отвечал на вопросы по Java, опираясь на знания Python из университета.
Мне предложили работу за 30 000 рублей параллельно с учёбой. После испытательного срока зарплата выросла до 50 000. Через год компания закрылась, и я вышел на широкий IT-рынок. С тех пор было много работы, увольнений и переездов. И вот я здесь!

📣 Теперь делюсь опытом


На этом канале я рассказываю о работе Java/Kotlin Backend разработчика и помогаю новичкам найти первую работу в IT. Делюсь своим опытом, чтобы ваш путь был легче и интереснее.
Нужна консультация? Пишите в личку @stepoleggg — это бесплатно!
👍21
Как устроиться Java разработчиком?
Вот пошаговый план:

1. Изучить основы синтаксиса Java

— Переменные и основные типы (Integer, Double, Boolean, Long, String)
— Условия, циклы (if, else, for, while)
— Коллекции (ArrayList, LinkedList, HashSet, HashMap, Array)
— Классы и ООП (Методы, классы, конструкторы, наследование, переопределение, перегрузка, интерфейсы, абстрактные классы, static методы и переменные)

2. Изучить основы SQL

— Базовые операторы (select, update, delete, insert)
— Джойны (left/right/inner join)
— Условия, группировки, сортировки (where, group by, order by, having)

3. Написать простой сервис на Spring Boot + Spring Data JPA + PostgreSQL

Большая часть всех сервисов в работе это сохранение, обновление, чтение данных в БД. Можно написать приложение TODO лист - список задач. В случае проблем с конфигурацией используй ChatGPT - он отлично умеет конфигурировать такие базовые проекты

4. Найти ментора

Без него никак. Попроси провести тестовое собеседование. Он должен оценить твои знания и сказать какие темы нужно подтянуть. Многие менторы готовы помогать бесплатно. Также можно попросить сделать ревью кода из предыдущего шага. Это будет очень полезно для более легкого старта на работе

5. Написать резюме

Здесь нужно накрутить минимум 1 год опыта. К сожалению, без него никто не будет звать тебя на собеседования. Попроси ментора помочь с легендой и оформлением резюме. Нужны правдоподобные объяснения что и как ты делал. Очень важно проработать правдоподобные ответы на несколько уровней в глубину

6. Заполнить профиль на hh.ru и откликаться на вакансии

Здесь ничего сложного. На 100 откликов тебе должны написать 5-10 раз. Если никто не пишет, то попробуй улучшить резюме вместе с ментором

7. Проходить собеседования


Первые собесы всегда будут провалены. Но с каждым следующим ты будешь сильнее. В среднем, каждые 5-10 собеседований дают 1 оффер. Оффер - предложение по работе

8. Соглашаться на лучший оффер, что у тебя будет на руках

Отказывайся, только если компания или вакансия реально плохие. На первое время подойдет почти любой вариант. Перед отказом и согласием рекомендую посоветоваться с ментором. Так проще избежать ошибок в построении карьеры на начальном этапе

9. Выйти на работу и вникать в процессы на ходу

Обычно испытательный срок длится 3 месяца. 80% людей его преодолевают. Так что шансы провала небольшие. В следующих постах расскажу что и как нужно делать на работе в первые дни, недели, месяцы, чтобы не быть уволенным

Где найти ментора?

Пиши мне - @stepoleggg. Я бесплатно помогу проработать твой личный план действий, исходя из твоих ситуации, целей и сроков.

Также можешь найти менторов в телеграм боте Найти ментора (не реклама). Там очень много наставников, которые могут помочь за деньги и бесплатно
👍4
Алгоритмы, которые точно пригодятся на собеседованиях

Для прохождения собеседования в среднюю IT компанию не нужно сильно погружаться в Leetcode задачи или читать объемные книги по решению алгоритмических задач. Достаточно разобраться и понять суть нескольких широко известных (и горячо любимых на собесах) алгоритмов.
Вот что тебе нужно знать:

1. Алгоритмы сортировки
— Быстрая сортировка (QuickSort)
— Пузырьковая сортировка (BubbleSort)

2. Алгоритмы поиска
— Бинарный поиск
— Линейный поиск

3. Алгоритмы на графах
— Поиск в глубину (DFS)
— Поиск в ширину (BFS)

4. Рекурсия
— Числа Фибоначчи - здесь важно понимать принцип как работает рекурсия, в чем ее минусы и какие есть альтернативы

По всем алгоритмам нужно уметь объяснить основной принцип работы, знать и уметь обосновать их алгоритмическую сложность (O-нотация) по скорости и по памяти

Вот примеры реализации алгоритмов из списка на Java:

Github: https://gist.github.com/coderroleggg/22aa406674e9cadf0406d0244961eb18
Notion: https://javaoleggg.notion.site/10679370ba59809caf17c0cfa477c54e
👍1
Нужно ли делать пет-проекты начинающему Java разработчику?

В целом, если вы хотите найти первую работу максимально быстро, то можете не делать никакие пет-проекты и перейти сразу к этапу собеседований. Однако В backend разработке есть несколько важных вещей, которые сложно понять, изучая только теорию. Без них первые дни на работе будут более сложными.

Зачем делать пет-проект:

— Можно пощупать типичное устройство бэкенд приложений в спокойном темпе. На примере простого пет-проекта можно по верхам изучить как запускаются, конфигурируются и тестируются проекты, похожие по своей сути на реальные рабочие сервисы.

— Процесс код ревью. Если попросить ментора прокомментировать твой код, то можно легко выявить множество ошибок начального уровня, чтобы не допустить их в будущем на первой работе. А также процесс код ревью научит тебя базовым командам git, которые действительно используются в компаниях при работе с ветками и мерж реквестами

Какой пет-проект лучше сделать?

Для первого пет-проекта в стиле CRUD API (create, read, update, delete) я предлагаю сделать бэкенд для приложения TO-DO List. Оно понятное, минималистичное, не требует сложных технологий. В этом приложении пользователь может всего лишь создавать, изменять, читать и удалять списки задач.

В качестве технологий используй такой стек:
— Spring Boot
— PostgreSQL
— REST API
— Spring Data JPA

Внутри проекта должны быть слои:
— controller
— service
— repository

Также нужно будет использовать Entity сущности для работы с базой данных

Что делать после написания проекта?

— Создать репозиторий в GitHub под проект, запушить проект в отдельную ветку (не main) и создать pull request из твоей ветки в ветку main
— Найти ментора и попросить провести код ревью, скинув ссылку на pull request
— Разобрать и исправить свои ошибки
— Получить свой первый approve на код ревью. Смержить код в ветку main

🔗 Полное техническое задание с подробно описанными требованиями по проекту TO-DO List смотри здесь

Если есть вопросы:

Пиши мне, я помогу: @stepoleggg
👍2
В чем разница между LinkedList и ArrayList?

— LinkedList - это двусвязный список, реализующий интерфейсы List и Deque. Он представляет собой последовательность элементов, где каждый элемент хранит ссылки на предыдущий и следующий элементы.

— ArrayList тоже хранит в себе неограниченную последовательность элементов. Однако внутри он устроен немного иначе. Под капотом у него находится обычный массив фиксированной длины, который по мере добавления элементов в ArrayList копируется в более длинную версию, если в нем заканчивается место.

В целом, если ты просто хочешь хранить и обрабатывать список элементов, ArrayList будет хорошим выбором по умолчанию. Однако есть ситуации, когда LinkedList может быть более эффективным.

Зачем использовать ArrayList:
— Когда нужен быстрый произвольный доступ к элементам по индексу.
— Когда ты в основном выполняешь операции чтения и редко вставляешь/удаляешь элементы в середине списка.
— Когда важна экономия памяти (ArrayList занимает меньше места в памяти).

Зачем использовать LinkedList:
— Когда ты часто вставляешь или удаляешь элементы в начале или середине списка.
— Когда тебе нужна двусторонняя очередь (Deque) - LinkedList реализует интерфейс Deque.
— Когда размер коллекции часто меняется и важна эффективность изменения размера.

🔗 Подробное сравнение производительности ArrayList и LinkedList для всех операций

Если есть вопросы:

Пиши мне, я помогу: @stepoleggg
👍4
3 главные ошибки при работе с многопоточностью в Java

Многопоточность — мощный инструмент, но при неправильном использовании может стать источником сложных багов, которые крайне трудно воспроизвести и корректно обнаружить во время тестирования.

Давай заглянем в три самые распространенные ошибки, которые подстерегают тебя при работе с потоками, чтобы хотя бы минимально защититься от них в своих проектах.

1. Гонка потоков (Race Condition)


— Это ситуация, когда несколько потоков одновременно пытаются изменить общие данные.
— Проблема возникает из-за непредсказуемого порядка выполнения операций разными потоками.
— Может привести к потере или искажению данных.

Решение: использование ключевого слова synchronized или других механизмов синхронизации (например, Lock, AtomicInteger).


2. Взаимная блокировка (Deadlock)

— Возникает, когда два или более потока блокируют ресурсы друг друга и ожидают их освобождения.
— Приводит к "зависанию" программы, так как потоки не могут продолжить выполнение.

Решение: правильный порядок получения блокировок, использование tryLock() с таймаутом, избегание вложенных синхронизаций.


3. Неэффективное создание потоков

— Создание нового потока для каждой задачи может быть ресурсоемким.
— Большое количество потоков может привести к снижению производительности.

Решение: использование ExecutorService для управления пулом потоков.


В целом, многопоточное программирование требует внимательности и понимания принципов работы потоков. Использование высокоуровневых абстракций из пакета java.util.concurrent (например, ConcurrentHashMap, AtomicInteger) часто помогает избежать типичных ошибок и сделать код более надежным и эффективным.

Также стоит помнить, что на junior/middle собеседованиях обычно не требуют идеальное знание многопоточности. Я не рекомендую слишком много внимания уделять этой теме при подготовке к собеседованиям. Однако знакомство с основными принципами, классами и ошибками в многопоточности необходимо.

🔗 Примеры ошибок и исправленного кода

Если есть вопросы:


Пишите мне, я помогу: @stepoleggg
👍4
Что нужно знать об индексах для собеседования на backend разработчика?

Почти на каждом собесе так или иначе затрагивают тему индексов. Однако вообще не очевидно какой глубины вопросов ждать и что конкретно нужно знать.

Поэтому исходя из своего опыта прохождений собесов я решил написать этот гайд по всем деталям и нюансам индексов, которые достаточно выучить, чтобы успешно проходить эту часть технического интервью в любой компании.

Вот что тебе нужно знать:

Что такое индексы?

Индексы в SQL - это специальные структуры данных, которые улучшают скорость выполнения запросов путем оптимизации поиска и сортировки данных. Правильное использование индексов может значительно повысить производительность базы данных.

Как устроены индексы?

Индекс - это отдельная структура данных, которая содержит значения одного или нескольких столбцов таблицы вместе с указателями на соответствующие строки данных. Индекс не хранит полную копию данных, а скорее организует значения выбранных столбцов в структуру, оптимизированную для быстрого поиска.

— Каждая запись в индексе включает указатель (RowID или TID) на соответствующую строку в таблице.
— Индекс обычно хранится в отсортированном виде, что позволяет быстро находить нужные значения.

Как работают индексы?

Индексы работают по принципу, схожему с оглавлением книги. Вместо просмотра всей книги (таблицы) страница за страницей, вы можете быстро найти нужную информацию, используя оглавление (индекс).

Когда выполняется запрос с условием на индексированный столбец, СУБД сначала обращается к индексу, находит соответствующие значения, а затем использует связанные указатели для быстрого доступа к полным строкам данных в таблице.

Типы индексов по назначению

— Кластерные индексы - физически сортируют реальные данные, в PostgreSQL их нет. Однако есть команда CLUSTER, которая позволяет физически переупорядочить таблицу на основе индекса.

— Некластерные индексы - обычные индексы в PostgreSQL.

— Уникальные индексы - гарантируют уникальность значений в индексируемом столбце или комбинации столбцов.

— Составные индексы - индексы, созданные на нескольких столбцах.

— Частичные индексы - создаются только для подмножества строк таблицы, удовлетворяющих определенному условию.

— Функциональные индексы - созданные на результате выполнения функции над столбцом(ами).

Типы индексов по устройству

PostgreSQL поддерживает несколько физических типов индексов, каждый из которых имеет свои особенности и области применения:

1. B-tree (сбалансированное дерево)

— Тип по умолчанию, если не указан явно
— Поддерживает сортировку и диапазонные запросы
— Эффективен для сравнений с использованием <, <=, =, >=, >

2. Hash

— Эффективен только для сравнений на равенство
— Может быть быстрее B-tree для простых проверок на равенство
— Не поддерживает сортировку или диапазонные запросы

3. И другие типы
— GiST
— GIN
— SP-GiST
— BRIN

Нюансы использования индексов

Преимущества индексов:

— Ускорение поиска данных
— Оптимизация сортировки
— Улучшение производительности JOIN-операций

Недостатки индексов:

— Занимают дополнительное место на диске
— Замедляют операции вставки, обновления и удаления
— Требуют обслуживания (перестроение, дефрагментация)

Когда использовать индексы:

— На столбцах, часто используемых в условиях WHERE
— На столбцах, участвующих в JOIN
— На столбцах, используемых в ORDER BY и GROUP BY

Когда не использовать индексы:

— На маленьких таблицах
— На столбцах с низкой кардинальностью (мало уникальных значений)
— На столбцах, которые часто обновляются

Оптимизация запросов с использованием индексов

— Используй EXPLAIN для анализа выполнения запроса и проверки использования индексов:
— Создавай индексы, которые включают все столбцы, необходимые для выполнения запроса:
— Если ты часто используешь функции в WHERE условиях, тогда создавай индекс на результат функции:

🔗 Примеры SQL команд на создание всех типов индексов

Если есть вопросы:

Пиши мне, я помогу: @stepoleggg
🔥2
API: все что нужно знать для собесов

На собесах я часто встречаю кучу вопросов, связанных с API. Они не сильно далеки друг от друга, все они крутятся вокруг базовых понятий: REST, HTTP и редко отходят в сторону. Поэтому довольно просто выучить блок вопросов по теме API и больше не тупить на большинстве интервью, если речь зашла об интеграции программ.

Я решил собрать все нужные для собесов знания в этом посте. Вот что тебе реально нужно знать:

Что такое API?


API (Application Programming Interface) - это набор определений, протоколов и инструментов для интеграции программного обеспечения. API определяет, как различные компоненты программного обеспечения должны взаимодействовать друг с другом.

Какие типы API бывают?

— REST: архитектурный стиль, использующий HTTP протокол для обмена данными.
— SOAP: протокол обмена структурированными данными в распределенной вычислительной среде.
— GraphQL: язык запросов и манипулирования данными для API, позволяющий клиентам точно указывать, какие данные им нужны.
— gRPC: высокопроизводительный фреймворк для удаленного вызова процедур (RPC).
— WebSocket: протокол, обеспечивающий такую связь между клиентом и сервером, где и сервер, и клиент могут слать запросы друг другу в рамках 1 соединения.

Что такое RESTful API?

REST - это архитектурный стиль для разработки веб-сервисов. RESTful API следует принципам REST.

Основные принципы REST:
— Клиент-серверная архитектура
— Stateless (без сохранения состояния): каждый запрос содержит всю необходимую информацию.
— Кэширование: ответы должны явно определять, можно ли их кэшировать.
— Единообразие интерфейса: унифицированный способ взаимодействия между компонентами.
— Многоуровневая система: архитектура может состоять из нескольких слоев.

Какие бывают HTTP-методы?

Вот самые распространенные HTTP-методы:
— GET: получение данных, не имеет body в запросе
— POST: создание новых ресурсов
— PUT: полное обновление существующего ресурса
— PATCH: частичное обновление ресурса
— DELETE: удаление ресурса

Что такое аутентификация и авторизация?

Аутентификация — это процесс подтверждения личности пользователя (например, ввод логина и пароля), а авторизация — это предоставление прав доступа к ресурсам на основе подтвержденной личности.

Методы обеспечения аутентификации и авторизации:

— API ключи: аутентификация, где клиент использует уникальный ключ для доступа к API.
— OAuth 2.0: протокол авторизации, который позволяет получать ограниченный доступ к учетным записям пользователей.
— JWT (JSON Web Tokens): безопасный способ передачи информации между сторонами в виде JSON-объекта.
— Basic Auth: метод аутентификации, использующий просто имя пользователя и пароль в запросе.
— API Gateway: централизованный сервис для управления аутентификацией и авторизацией.

Как работать с разными версиями API?

Лучше всего версионировать через URL: /api/v1/users, /api/v2/users
Но можно также указывать версию в параметрах или header'ах запроса

Как документировать API?

Можно генерировать html-страницу-документацию с помощью Swagger. Ее можно будет также использовать для тестирования API. Еще важно всегда предоставлять примеры запросов-ответов и возможных ошибок

Как можно оптимизировать API?

— Кэшировать
— Сжимать данные (например, gzip)
— Использовать CDN для статического контента
— Обрабатывать запросы асинхронно

🔗 Дополненный гайд по API + примеры вопросов и ответов на собеседовании

Если есть вопросы:

Пиши мне, я помогу: @stepoleggg
🔥5
Как делать тестовые задания, чтобы потом не сожалеть?

В некоторых компаниях первым этапом отсева кандидатов является тестовое задание - домашняя работа, где ты делаешь небольшой проект или решение определенной задачи. Такое задание позволяет работодателю увидеть, насколько хорошо ты пишешь код с нуля и соблюдаешь ли best-practices.

Вот список того, что тебе нужно знать о тестовых заданиях, прежде чем приступать за выполнение:

Чего ожидать от тестового задания?

— Задание на создание REST API или реализацию сложного алгоритма: чаще всего это небольшой сервис или функция, которую нужно разработать.
— Срок выполнения 3-7 дней: у тебя будет ограниченное время, поэтому важно правильно распределить усилия.
— Использование определенного стека технологий: обычно просят использовать Java + Spring Boot + PostgreSQL.
— Требования по тестам и документации: работодатели ценят наличие unit-тестов и хорошей документации к коду.

Что можно сделать заранее?

— Настроить базовую структуру проекта через Spring Initializr: это сэкономит время на начальном этапе разработки.
— Подготовить базу данных: установи и настрой H2 или PostgreSQL в Docker, создай простой CRUD-контроллер для практики - он будет заготовкой для будущего тестового задания - проекта, сможешь его переиспользовать.
— Создать базовый README-файл: опиши, как запустить и использовать твой проект.
— Купить подписку на ChatGPT

На что обратить внимание при выполнении задания?

— Добавить unit-тесты: без них ты точно провалишь этот этап, они нужны обязательно
— Реализовать валидацию входных данных: проверяющим понравится, что ты так внимательно подходишь к обработке всех возможных случаев, даже не совсем корректных
— Настроить Docker и docker-compose: это облегчит запуск проекта на стороне проверяющего и покажет, что ты уверенный пользователь докера🐳
— Дополнить документацию примерами API-запросов: это упростит понимание и тестирование твоего сервиса. Чем понятнее твой проект для проверяющего, тем выше шансы, что ты пройдешь этап. Все должно работать как в новом айфоне - с 1 кнопочки (можешь использовать docker compose для этого, он хорош, когда тебе нужно поднять большой стек сервисов в 1 клик)

! Используй ChatGPT


Сегодня многие задачи можно решить чуть ли не с первой попытки с помощью последних версий нейронок: ChatGPT 4o или Claude 3.5 Sonnet. Они могут помочь с генерацией кода проекта. Однако не забывай проверять что оно вообще работает :)

Стоит ли вообще выполнять тестовые задания?

Выполнение тестовых заданий требует времени и не гарантирует трудоустройство. Имеет смысл тратить на них время, если у тебя нет альтернатив или ты действительно заинтересован в конкретной компании. Базовое правило - всегда взвешивай чего тебе стоит то или иное действие, а также что оно дает. Тестовое задание в проходной компании может давать лишь сомнительную возможность попасть хоть куда-то. Однако в каком-то другом случае может быть наоборот очень оправданным, если ты идешь в компанию мечты на очень релевантную вакансию. Действуй с умом и не трать время зря, если знаешь чего хочешь. Удачи!

🔗 Примеры тестовых заданий

🔜 Я решил собрать все необходимые знания о собеседованиях в серии постов, которая будет выходить на этом канале. В следующей части поговорим о HR-скринингах

Если есть вопросы:

Пиши мне, я помогу: @stepoleggg
👍3
🐳 Docker: что должен знать backend разработчик?

Docker уже давно стал стандартом в индустрии, и в 2024 году знать и уметь использовать его нужно обязательно. Если ты все еще не пробовал докер, то пора начинать. Я уверен, он тебе даже понравится. Реально удобная штука)

В этом посте я накидал шпаргалку по Docker, чтобы не забывать основные нюансы и детали в докере. Вот что важно помнить:

Что такое докер?

Docker упаковывает и запускает контейнеры.

Контейнер - заключает в себе изолированное приложение, которое работает одинаково на всех компьютерах с Docker платформой. Однако он легче и быстрее виртуальных машин, потому что не виртуализирует оборудование (как это делает виртуальная машина). Контейнер запускается из собранного или скачанного Docker образа.

Образ - это файл, используемый для создания контейнера. Он содержит все библиотеки, зависимости и файлы, нужные для запуска контейнера. Например, можно иметь образ базы данных PostgreSQL или самостоятельно собранный образ твоего бэкенд приложения на Spring. Из этих образов можно поднимать сколько угодно контейнеров, которые будут работать в соответствии с образом.

Docker daemon - это сердце системы. Он управляет всеми контейнерами.

Registry - это репозиторий докер-образов. Docker Hub - самый популярный, но многие компании используют свои приватные registry. В них хранятся образы, которые можно пушить, скачивать и запускать с помощью Docker платформы.

Docker на каждый день

Полезные команды:

— docker build - собрать образ
— docker run - запустить контейнер
— docker stop - остановить контейнер
— docker logs - посмотреть логи
— docker ps - посмотреть контейнеры

Что настраивается часто:

— Настройка портов для внешних запросов к контейнеру (port mapping) (-p 8080:80) - это как настроить переадресацию звонков: снаружи звонят на один номер, а попадают на другой.
— Подключение внешних volumes - это как внешний жесткий диск для твоего контейнера, данные останутся даже после его удаления.

Как собирать образы?

Чтобы собрать образ нужно описать поэтапный процесс сборки и запуска образа в специальном файле - Dockerfile. Затем при выполнении команды docker build образ будет собран в соответствии с инструкцией и сохранен в локальном репозитории образов.

Важные моменты в сборке образов:

— Помни про разные версии базовых образов - alpine/slim/full. Всегда важен баланс между размером и функциональностью образа. Часто full может быть слишком большим, а alpine не совсем совместим.
— Используй multi-stage build. Такой подход позволяет собирать приложение в несколько этапов, уменьшая размер докер образа. Например, можно компилировать в одном контейнере, а запускать в другом, более легком.
— В сборке образа применяется кэширование, которое пропускает часть инструкций по порядку сверх вниз, если исходные данные не изменились. Такой кэш слоев в Dockerfile нужно использовать с умом. Редко меняющиеся инструкции размещай вверху. Часто меняющиеся - внизу.
— Используй .dockerignore и удаляй ненужные файлы, чтобы уменьшить размер образа.

Зачем нужен Docker Compose?

Docker Compose - это пульт управления связанными контейнерами:

— Вместо запуска 5-6 команд в терминале, ты пишешь один файл и запускаешь всё одной командой.
— Переменные окружения можно хранить в .env файле - никаких больше захардкоженных паролей.
— Конфигурация запускаемой связки приложений обычно находится в файле docker-compose.yml

С чего лучше начать?

1. Оберни все свои пет-проекты и любые отдельные файлы-скрипты в докер-образы, чтобы запускать их только в виде Docker-контейнеров.
2. Замени все базы данных на Docker-контейнеры с базой данных, отделив сами данные в volume.
3. Проекты, где есть как минимум 1 сервис и база переведи на docker compose, чтобы запускать весь стек за 1 команду в терминале

🔗 Примеры Dockerfile + примеры использования самых полезных команд в Docker

Если есть вопросы:

Пиши мне, я помогу: @stepoleggg
🔥2
Как работать с микросервисами на реальных проектах? Мой опыт

Почти все разработчики любят внедрять "крутые" технические решения.
Однако с любыми резкими действиями на большом проекте нужно быть аккуратнее. Перед совершением каждого шага по изменению существующей архитектуры нужно тщательно продумать причины и риски совершаемого действия.

Одним из популярных технических решений является переписывание монолита на микросервисы. В этом посте предлагаю погрузиться в тему микросервисов и хотя бы частично разобрать логику, по которой стоит (на мой взгляд) действовать, если ты хочешь прикоснуться к микросервисам. В основе поста мысли во время ретро, реальные баги с прода и выводы после долгих обсуждений с коллегами!

Что такое микросервисы?

Микросервисная архитектура - это подход к разработке, при котором приложение строится как набор небольших сервисов, каждый из которых:

— Выполняет одну бизнес-функцию
— Работает независимо
— Взаимодействует с другими сервисами через API

Зачем использовать микросервисы?

— Чтобы независимо масштабировать части системы
— Чтобы релизить разные сервисы независимо друг от друга
— Чтобы упростить код, разделив его на независимые части

В какой момент стоит начать делить монолит на микросервисы?

Когда в твоем сервисе есть проблемы из списка:

— Слишком много изменений в одном сервисе разрабатываются параллельно. Возникают проблемы с тем, что непротестированные фичи блокируют релиз готовых задач.

— Сервису не хватает вычислительных ресурсов/памяти. И при этом есть самый жирный процесс внутри сервиса, который и забирает на себя почти все эти ресурсы, а остальные процессы не настолько требовательны.

— Код сервиса стал слишком сложным, что возникают проблемы при реализации новых задач. Любые изменения в одном бизнес-процессе могут случайно иметь неожиданные последствия в других бизнес-процессах.

Какой размер сервиса оптимален?

Такой, при котором сервис будет решать одну бизнес-задачу. Нет смысла делить слишком мелко.

Например, сервис для форматирования телефонов будет слишком маленьким. При таком делении, сервис не будет решать проблемы:

— Изменения в нем будут происходить очень-очень редко (так как логика очень мелкая)

— Масштабировать такой сервис независимо от других не понадобится, так как он не будет выполнять отдельную бизнес-функцию. Он будет масштабироваться всегда в связке с другими сервисами

— Код вызывающего его сервиса не упростится, так как простая валидация телефонов напрямую в большом сервисе заменится на простой вызов API сервиса валидации телефонов

Однако такой сервис будет создавать дополнительные риски:

— Разрыв соединения между сервисами
— Недоступность сервиса

Как организовать взаимодействие между сервисами?

— Синхронное. Если тебе нужно получить мгновенный результат операции, то по-умолчанию используй REST. Использование других протоколов должно быть оправдано спецификой конкретной задачи. Лучше не брать, например, какой-нибудь gRPC просто потому что хочется.

— Асинхронное. Если результат прямо сейчас не нужен. В больших компаниях любят использовать Apache Kafka. Ее знание может быть полезно на собесах

Что нужно поднять по инфраструктуре?

— Логи: ELK/Graylog/Splunk. Без логов твои сервисы будут черным ящиком, невозможно будет понимать что происходит внутри. Желательно уметь работать с какой-нибудь системой логов, чтобы быстро разбирать баги и мониторить ошибки.

— Мониторинг: Prometheus + Grafana. Метрики позволяют отслеживать как технические, так и бизнес значения по времени. Также на метриках можно удобно настроить алерты, которые разбудят тебя ночью, если сервис упал.

— Трейсинг. Еще один способ капнуть глубже и узнать что конкретно вызывается внутри твоего сервиса. Но я не особо использую это в реальной работе.

— CI/CD: Jenkins/GitLab + K8s. Автоматизации и пайплайны позволяют быстро и удобно катить версии на тестовые и продакшн контуры. Если не было опыта написания таких, то можно потренироваться, настроив пайпы для пет-проекта через Github Actions

🔗 Чеклист по разделению монолита на микросервисы

Если есть вопросы:

Пиши мне, я помогу: @stepoleggg
👍1
8 ошибок в тестировании кода

Многие разработчики стремятся как можно быстрее завершить задачу и перейти к следующей. Однако без должного внимания к тестированию можно столкнуться с неожиданными проблемами в проде. В этом посте я поделюсь 8 распространенными ошибками в тестировании кода, на которые натыкался не раз лично или в команде (иногда из-за таких ошибок релиз задерживался на недели).

1. На новую фичу не пишутся тест-кейсы


Часто разработчики пропускают написание тестов для новых функций, что приводит к скрытым багам. Без тест-кейсов сложно убедиться, что новая функциональность работает корректно и не нарушает существующий код.

2. Непрошедшие тест-кейсы удаляются или правятся без понимания причин

Если тест внезапно перестал проходить, важно разобраться в причине. Просто удалить или изменить тест, чтобы он прошёл, — плохая практика. Это может скрыть серьёзные проблемы в коде. Важно всегда быть внимательным на код-ревью и не пропускать такие мерж реквесты.

3. "Срочный" фикс бага делается без написания соответствующего тест-кейса

Исправляя баг в спешке, можно забыть о написании теста, подтверждающего исправление. Без такого теста есть риск повторного появления бага в будущем. Лучше договориться и зафиксировать идею в команде разработчиков, что на любой фикс должен быть написан сначала тест-кейс, воспроизводящий баг. Иногда баг может и не существовать вовсе.

4. Фикс бага делается наугад "авось поправится"

Попытка исправить проблему без полного понимания её причин может привести к новым ошибкам. Всегда старайся детально разобраться в сути бага перед его исправлением. Если ты не понял почему система работает не так как нужно, то не начинай исправлять код, а подумай и поисследуй еще раз более внимательно.

5. Игнорирование проваленных тестов

Запуская тесты и видя, что некоторые из них не проходят, нельзя просто закрыть на это глаза. Даже если кажется, что всё работает, проваленные тесты могут указывать на скрытые проблемы. Решается через настроенный СI-пайплайн, который обязательно прогоняет тесты и не дает смержить код без успешно пройденных тестов.

6. Непокрытые тестами негативные сценарии

Ограничиваясь тестированием только основных сценариев, можно пропустить ошибки в крайних случаях. Важно стремиться к максимальному покрытию кода тестами, включая негативные сценарии. О них разработчики часто забывают или тестируют на пол шишки

7. Зависимость тестов друг от друга

Когда тесты зависят от результата предыдущих, это усложняет их поддержку и может привести к ложным срабатываниям. Тесты должны быть независимыми и воспроизводимыми в любом порядке. Это база.

8. Использование реальных данных и сервисов в тестах

Привязка тестов к реальным базам данных или внешним сервисам делает их медленными и ненадёжными. Лучше использовать мок-объекты или фиктивные данные для изоляции тестовой среды. Это я проговариваю на всякий случай.

Как избежать этих ошибок?

— Интегрируй тестирование в процесс разработки: пиши тесты одновременно с кодом или до (TDD).
— Анализируй результаты тестов: разбирайся в причинах провалов и исправляй их осознанно.
— Обучайся и делись опытом: обсуждай с коллегами лучшие практики тестирования, уточняй непонятные моменты, связанные с тестами (их всегда очень много).
— Используй инструменты автоматизации: настрой CI/CD для автоматического запуска тестов.

🔗 Примеры кода тестов

Если есть вопросы:

Пиши мне, я помогу: @stepoleggg
👍2
Как работать с исключениями?

Многие разработчики относятся к обработке исключений как к досадной необходимости. Однако грамотная работа с ошибками часто спасает от нарушения бизнес логики в программе и даже от убытков компании, которые она может понести из-за неправильной работы приложения.

Сегодня поделюсь практическими советами по работе с исключениями в Java.

Что такое исключения?

Исключения в Java - это объекты, которые создаются при возникновении ошибочной ситуации. Они позволяют:
— Отделить код обработки ошибок от основной бизнес-логики
— Передать информацию об ошибке вверх по стеку вызовов
— Предоставить детали о месте и причине сбоя

Когда создавать собственные исключения?

Стоит создавать свои исключения, когда в приложении есть:
— Специфичные бизнес-ошибки (например, недостаточно денег на счете). Стандартные исключения не передадут нужный контекст и усложнят понимание кода.
— Особые технические сбои, требующие отдельной обработки. Такие исключения помогут быстрее локализовать и исправить проблему.

Как правильно обрабатывать исключения?

Следуй этим принципам:
— Перехватывай конкретные исключения вместо общего Exception. Это позволит по-разному обрабатывать разные ошибки и не пропустить неожиданные проблемы.
— Не оставляй пустых блоков catch. Молчаливое проглатывание ошибок - прямой путь к трудноуловимым багам в продакшене.
— Логируй исключения с полным стектрейсом: logger.error("Ошибка ...", exception). Без стектрейса при расследовании инцидента придется гадать, где именно произошла ошибка.
— Не используй исключения для управления потоком выполнения программы. Эт делает код трудным для понимания.

Какую иерархию исключений выбрать?

Оптимальный подход:
— Создай базовые классы для разных категорий ошибок. Четкое разделение типов ошибок поможет в их обработке и мониторинге.
— Используй checked exceptions для ожидаемых ошибок. Компилятор заставит явно обработать такие случаи.
— Используй unchecked для программных ошибок. Эти ошибки обычно говорят о баге в программе и требуют исправления кода.

Как логировать исключения?

Важные моменты при логировании:
— Используй разные уровни логирования. ERROR должен действительно означать проблему, требующую внимания, иначе важные ошибки потеряются в потоке логов и только замусорят их.
— Добавляй контекст (ID пользователя, параметры запроса). Без контекста часто невозможно воспроизвести проблему или понять ее причину.
— Не дублируй логи в разных местах. Множественное логирование одной ошибки затрудняет анализ проблем и засоряет логи.
— Структурируй сообщения для удобного поиска. Хорошо структурированные логи можно эффективно фильтровать и анализировать.

Что я не советую делать при работе с исключениями:

— Использовать e.printStackTrace() в production коде. Этот метод пишет в System.err, что может привести к потере важной информации об ошибке.
— Игнорировать исключения без логирования. Если ошибка проглочена без следа, при проблемах в проде придется долго искать их причину.
— Перехватывать Throwable или Error. Error обычно означает серьезные проблемы, которые нельзя обработать (например, нехватка памяти).
— Возвращать null вместо выброса исключения. Null легко пропустить, что приведет к NullPointerException где-то далеко от места возникновения проблемы.

Полезные паттерны обработки исключений:

— Exception Translator: преобразование низкоуровневых исключений в бизнес-исключения. Помогает скрыть детали реализации и сделать API более понятным.
— Template Method: вынос общей логики обработки ошибок в базовый класс. Уменьшает дублирование кода и обеспечивает единообразную обработку ошибок.
— Chain of Responsibility: каскадная обработка разных типов исключений. Полезно когда разные части приложения должны по-разному реагировать на ошибки.

Если у вас есть вопросы или хотите обсудить тему глубже - пишите в лс! Поделюсь дополнительными примерами из практики.

🔗 Примеры кода обработки исключений в Java

Если есть вопросы по разработке и карьере в IT:

Пиши мне, я помогу: @stepoleggg
👍41