Новиков > путь в Big Tech – Telegram
Новиков > путь в Big Tech
184 subscribers
94 photos
192 links
От зеро-кодинга на стройке до написания высоконагруженных сервисов в Big Tech. 

Пишет SWE в Avito.ru (backend), в прошлом: .NET developer и сертифицированный специалист по использованию BIM.

Написать автору: @nvkv_ai

Книги: https://boosty.to/time2code
Download Telegram
Фичалидерство: почему это нужно каждому?

В зрелых кросс-функциональных командах существует понятие фича-лида.

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

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

У нас довольно зрелая команда, поэтому практически каждый инженер становится фича-лидом по той или иной задаче. Это позволяет сильно разгрузить тимлида и повысить качество каждой отдельно взятой функциональности.

Зачем это инженеру?

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

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

Что делает фичалид?

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

Разобьем процесс по этапам:

🌎 Дискавери

- Погружается в контекст: пытается понять что и для чего мы делаем. Какие метрики при достижении результата важны.
- Коммуникация с внешними командами для уточнения контекста.

🚚 Передача в разработку

- Помощь тимлиду в составлении роадмапа на квартал
- Составление роадмапа по своей фиче и декомпозиция работ
- Обсуждение открытых вопросов на груминге, фасилитация процесса и фиксирование договоренностей
- При необходимости: организация встречи 3 Амиго
- Проработка рисков, моделирование угроз, обсуждение метрик и мониторинга фичи

🛠 Разработка

- Качественное описание задач и критериев приемки либо делегация и контроль перед взятием задач в работу
- Активное участие в планировании и взятии задач в спринт в соответствии с роадмапом
- Полное владение контекстом на время разработки, взаимодействие с внешними командами и саппорт коллег по возникающим вопросам
- Согласование изменений в контрактах и фиксация всех артефактов по результатам обсуждений
- Обнаружение проблем по ходу и предложение альтернативных путей реализации
- Обеспечение качества совместно с QA

🚀 Запуск

- Если есть АБ-эксперимент, то его заведение или контроль заведения
- Оповещение стейкхолдеров о запуске
- Запуск
- Поддержка запущенной фичи: реакция на баги, помощь коллегам по вопросам связанным с фичой

Как инженер, который активно занимается фича-лидерством, отмечу, что это отличный опыт, который нужно обязательно получать, если подворачивается возможность.

Особенно, если этот процесс вас пугает.

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

Но если вас что-то действительно пугает, то рекомендую начать это делать. Сразу заметите как начнете расти, а потом вас уже будет не остановить и это войдет в привычку.
🔥2
Что выведет программа?

Только не спрашивайте ChatGPT (он не справился).
package main

import (
"encoding/json"
"fmt"
)

type Data struct {
Value Arrays `json:"data"`
Name string `json:"name"`
}

type Arrays struct {
IntArray *[]int `json:"values,omitempty"`
}

func assignValues(values *[]int, name string) Data {
data := Data{
Value: Arrays{
IntArray: values,
},
Name: name,
}
return data
}

func main() {
sl := make([]int, 0)
sl = nil

data := assignValues(&sl, "nil")
value, _ := json.Marshal(data)

display(value)
}

func display(v []byte) {
fmt.Println(string(v))
}
🔥1
Простой язык программирования - иллюзия, которая продается

Изначально я хотел написать про Go, но, заметив, что большинство с ним незнакомо, решил обобщить.

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

Это отлично подходит для маркетинга и позволяет быстро и дорого продавать курсы.

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

Наглядные примеры из спорта:

👟 Бег

Это легко и просто. Научиться бегать может каждый, причем довольно быстро.

Но:

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

Шахматы

Изучить правила можно за 5 минут. Они умещаются на листке A4.

Но:

- Без стратегии и тактики сложно выигрывать и получать удовольствие от игры.

В мире технологий также.

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

Уверен, что подобными деталями изобилует каждый язык.

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

Но чтобы писать хороший код, который содержит минимум багов, легко читается и поддерживается - потребуются годы. Это приходит с опытом, в этом и заключается мастерство.
👍9🫡32
Август 2024:

навыки

✔️ Приступил к изучению основ функционального программирования.
✔️ Попрактиковался с имутабельными состояниями в коде вместо передачи сущностей по ссылке.
✔️ Рассмотрел различные виды багов.
✔️ Научился локально разворачивать LLM с помощью ollama и подбирать языковую модель под конкретные задачи.
✔️ Записался на внутренние занятия по "Инженерной культере" и прошел 3, на которых изучил: какие механики предусмотрены для обеспечения стабильности систем (NFR, SLI, SLO, SLA и пр.); как обеспечивать качество и тестировать производительность системы, чтобы это вошло в привычку.
✔️ Решил разбирать каждую из глав книги System Design Interview и делать краткие зарисовки. Выложил 4 первые главы.

карьера

✔️ Согласовал вторую версию документа TDR для нового сервиса. В ближайшем спринте приступаю к реализации.
✔️ Провел сессию по моделированию угроз в команде для нового сервиса.
✔️ Принял роль фичалида перспективной функциональности.

посты

✔️ Как проводить моделирование угроз (читать)
✔️ Помогите AI-ассистентам, чтобы они помогли вам (читать)
✔️ Как читать техническую литературу (читать)
✔️ Функциональное программирование как другой взгляд на привычные вещи (читать)
✔️ Возможен ли катарсис в конце спринта (читать)
✔️ Почему вам нужно фичалидерство (читать)
✔️ ChatGPT бессилен перед классическим вопросом, а вы? (читать)

прочее

✔️ 2 забега на 10 км и 1 на 5 км с препятствиями. Лучший результат - 50:03 (5:00/км). Потихоньку улучшаем показатели. Следующая цель - полумарафон.

#результаты
👍8
Что делать, когда прямой путь не работает

Вчера ставил расширение на VS Code, которое поддерживается коллегами для внутреннего удобства разработки.

Простой плагин, который раскрашивает brief-файлы (корпоративный стандарт для описания контрактов по типу protobuf, подробнее почитать здесь), подкинул очередную задачку.

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

К плагину прилагалась простая инструкция:


1. Скачать `vsix` файл: wget http://<extension-directory>.vsix

2. Добавить его в VSCode: Extensions -> ... -> Install from VSIX -> <extension-directory>.vsix

3. Либо через терминал: code --install-extension <extension-directory>.vsix


Какое же мое разочарование было, когда я споткнулся на первом же шаге. Файл не существовал по нужному пути.

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

Далее рассудил так: если у меня есть репозиторий, куда активно контрибьютят, но нет самого vsix-файла (не важно, что я про такие файлы впервые слышу), который могу поставить как расширение, то что мне мешает собрать исходный код в такой файл?

Порадовавшись этой нехитрой мысли я переключился с проблемы "отсутствует файл", на задачу "как собрать файл". Через секунду я прочитал, что существует vsce - VS Code Extension Manager, с помощью которого я могу собрать нужный мне vsix. Ставим его:


npm install -g vsce


Далее переходим в директорию с исходником расширения (где живет наш плагин) и упаковываем в нужный vsix:


vsce package


Теперь нужно установить расширение в IDE. Используем следующую команду:


code --install-extension <extension-directory>.vsix


И, если с vsce все было просто, то следующим вызовом для меня как относительно нового пользователя VS Code стала команда code --instal-extension, так как просто в консоли она не выполнялась. Но спустя пару минут, разобравшись, что это CLI, уже устанавливал и ее.

В пределах 5-10 минут мне удалось: разобраться в проблеме, узнать как собирать vsix-файлы с помощью менеджера расширений vsce, устанавливать их через CLI и бонусом, пока все устанавливалось, подробно почитал про проект GNU Wget (небольшой тизер: Wget2 на подходе).

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

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

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

В Авито на уровень E5 (синьор) ее признаки обозначаются как:

- Улучшает общие инженерные инструменты компании.
- Тестирует сложные корнер-кейсы.
- Проектирует тестопригодные системы и исправляет те, которые сложно тестировать.
- Ищет неэффективные места в коде/архитектуре/тестовых моделях. Пополняет технический бэклог команды.
- Устанавливает и тестирует нефункциональные требования или привлекает для этого экспертов.
- Знает и использует безопасные подходы к реализации функциональности.


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

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

На днях подключал к новому сервису библиотеку для работы с АБ-экспериментами. Была понятная дока с примерами, а еще были интересные нюансы:

1. Либу можно было использовать несколькими способами. Один из них - подключение как мидлвара (middleware).

Когда сделал все в соответствии с документацией, то ничего не заработало. Я долго не понимал почему, перечитывал доку и комментарии к функциями, все было правильно, но не работало.

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

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

Как итог, все заработало.

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

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

2. При использовании либы, как мидлвары, оказалось, что ее нельзя адекватно протестировать юнит-тестами.

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

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

В юнитах - мы с мидлварой не работаем непосредственно, а прямое обогащение контекста предусмотрено не было.

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

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

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

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

Это дает приятно чувство удовлетворения, делает тебя немного счастливее и сильнее как инженера.

@time2code
👍72👏1
Лучше маленький шаг, чем грандиозный план. Создаем красивый блог за 10 минут.

За всю карьеру не вспомню ни одного момента, когда я не думал о создании своего сайта/блога. Даже, когда был рядовым инженером, была идея завести свою страницу-портфолио, куда смогу публиковать проекты, над которыми работал. В этом году даже ставил себе одну из целей - завести собственный блог на отдельном домене.

Шло время, но ничего не двигалось, так как я понимал, что нужны ресурсы на это: время и деньги для инфраструктуры.

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

Был даже план по написанию своего блога:

1. Стать фулстек-разработчиком, пройдя несколько курсов, изучая архитектуру фронтенда и JS
2. Мигрировать на стабильный VPS-сервер
3. Купить домен (тот, который нравится, стоит 10к в год)
4. Разработать блог

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

И вот в один прокрастинирующий вторник (вчера) я решил, что откладывать больше нельзя.

У многих профессиональных разработчиков на сайтах или блогах я видел приписки "powered by ..." - и все в этом духе. Это означает, что они используют какое-то внешнее решение для своего сайта/блога.

И тут я подумал: чем я хуже?

Сперва планировал использовать bear blog, так как нравился по стилю. Но то ли я не разобрался из-за вечерней усталости, то ли еще что, увидев необходимость регистрироваться на платформе, решил, что мне это не подходит.

На втором месте у меня в закладках был Hugo - часто видел его у других авторов.

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

Решил попробовать, тем более у проекта - отличная документация.

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

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

Сперва подумал, что смогу задеплоить на своем VPS-сервере, но сообразил, что быстро и легко не смогу это сделать...

Меня выручил github.pages, где у меня уже жила страничка с CV. А Hugo даже предоставляет простую инструкцию, как задеплоить туда.

Настроив Github Actions, чтобы проект автоматически разворачивался при пуше в мастер, и залив первую версию, с удивлением обнаружил, что все завелось с первой попытки!

Честно говоря, был счастлив, что, наконец, сделал серьезный шаг в сторону создания своего блога.

Конечно, Hugo уже оброс приличным функционалом и потребуется время, чтобы научиться пользоваться фреймворком, но самое важное, что начало положено и дальше будет проще.

Результат здесь (пока там только интро из гитхаба).

В планах перенести туда профессиональный блог с избранными постами из телеграма + начать активно его развивать с постингом в LinkedIn.

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

На текущий момент кажется, что Hugo идеально подходит, но не исключаю, что через год перееду на полноценное standalone решение, если столкнусь с ограничениями.

Таким образом, фактически я создал свой новый сайт меньше чем за 10 минут + 30-60 минут потратил на чтение документации.

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

Интересно был ли у вас похожий опыт с блогами и на каком решении остановились в итоге?

@time2code
4👍3
Можем ли мы писать код без ошибок?

Мой ответ: нет.

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

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

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

Но все равно случилось интересное. В конце прошлого спринта мы, наконец, смогли начать интеграционно тестировать фичу.

На бэкенде было найдено 3 бага:

1. Приходил цвет в странной кодировке.
2. Пустой массив не возвращался в ответе.
3. Лимит на количество элементов в ответе применялся в неправильном поле.

Что их объединяет? Все они были сделаны сознательно:

1. Вероятно, впервые я непосредственно работал с цветом. Поэтому совершенно не имел представления в какой кодировке его ожидает фронт. В ходе обсуждения задачи мы явно на это не обратили внимание, а я, в свою очередь, использовал внутренний механизм для этого и просто перекладывал кодировку из одной модельки в другую.
2. В данном случае промахнулись с критериями приемки и некорректно описали задачу, так как в ней было сказано опускать пустой ответ - так и было сделано, но фронтенд ожидал другое.
3. Самый интересный из описанных багов. Назовем его когнитивный, так как при прочтении задачи и сопутствующего контекста был сделан вывод сделать именно так, а не иначе.

У каждого бага есть своя предыстория, свой контекст и тип.

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

Задача каждого инженера - создавать понятную и точную спецификацию, от которой будет сложно отойти и наделать ошибок, но, к сожалению, это утопия.

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

Самое важное - делать правильные выводы, обсуждать все проблемы на ретро и формировать такие договоренности внутри команды, чтобы в будущем качество своего продукта держать на стабильно высоком уровне.

@time2code
👍5
Два года в Big Tech. Стоило того?

Вчера было ровно 2 года с момента, как я работаю в Авито.

За это время мне удалось качественно вырасти как специалист.

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

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

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

Попробую проанализировать плюсы и минусы, которые заметил за это время.

👍 Несколько причин стремиться в компании уровня Авито:

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

1. Инженерная культура

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

2. Зрелые процессы

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

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

3. Компенсация и коммьюнити

Сюда попадает то, что связано с гигиеническими факторами: зарплата, мед. страховка, дружелюбная среда и прочее.

По внутренним ощущениям и отзывам коллег - тут все отлично.

4. Привязанность к продукту

Еще важным критерием в пользу той или иной компании может стать личная привязанность к продукту.

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

Меня это очень сильно заряжает и дает по-настоящему большое удовлетворение от того, что делаю.

👎 Несколько причин избегать компании уровня Авито:

1. Медленный рост грейда

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

Тем не менее многое зависит от желания развиваться, компания предлагает множество возможностей для этого, которые нужно использовать.

2. Прозрачность решений топ-менеджмента

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

3. Потеря гибкости

Быстрорастущие компании постепенно теряют гибкость в процессах ради безопасности и былую атмосферу. Все это может отражаться на «счастье» сотрудников.

4. Маленькие задачи

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

И если в стартапе есть возможность с нуля построить MVP продукта, поработав сразу над множеством задач разной сложности, то в Биг Техе тебя легко могут посадить “красить кнопку” или “перекладывать json”.

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

@time2code
👍12
Сентябрь 2024:

развитие

✔️ Завершил курс «Основы функционального программирования».
✔️ На примере GORM (ORM для Go) рассмотрел, как можно увеличить производительность на 20% при работе с базой данных.
✔️ Прошел отбор на внутренний курс по "Инженерной культуре", который предполагает интенсивное погружение в такие темы, как "проектное управление", "системное мышление" и др.
✔️ Приступил к реализации нового сервиса, по которому в прошлом месяце утвердил TDR: подготовил инфраструктуру и начинаю разработку бизнес-логики.
✔️ Провел сессию по управлению рисками (+моделирование угроз) для важной функциональности в текущем квартале.
✔️ Обнаружил 3 бага в функциональности соседних команд: 1 в продукте и 2 в инфраструктуре. Один из них - коллеги уже исправили, а оставшиеся - запланировали на ближайшие спринты.

прочее

✔️ Пробежал первый полумарафон (21.1км) с результатом 1:50:54 (5:15/км).
✔️ Поставил личный рекорд на 10км - 47.28 (4:50/км).
✔️ Сходил на неделю в отпуск. Хайкинг в горах отлично помогает отвлечься от работы.

посты

🔖 Простой язык программирования - иллюзия, которая продается (читать)
🔖 Что делать, когда прямой путь не работает (читать)
🔖 Привычка, которая поможет расти в Big Tech (читать)
🔖 Лучше маленький шаг, чем грандиозный план. Создаем красивый блог за 10 минут (читать)
🔖 Можем ли мы писать код без ошибок? (читать)
🔖 Два года в Big Tech. Стоило того? (читать)

#результаты
🔥7👍2🫡2
Почему мозг избегает сложных задач. Как его заставить работать?

Мозг - самая энергозатратная часть нашего организма. Нередко он нас обманывает, но зачем?

Ответ банален: чтобы спасти нас, сберегая ресурсы для своей сложной работы.

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

Уверен, многие знакомы с концепцией: задавания вопросов. Есть поговорка: «хорошо заданный вопрос - половина ответа».

Но почему это работает и работает ли?

Зачем задавать вопросы и как это помогает решать задачи?

Сейчас прохожу курс по системному мышлению (СМ) и получаю множество инсайтов.

Кратко: СМ - учит двум вещам:
1. Видеть границы своей компетенции.
2. Видеть проблемы на большем масштабе.

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

Пока обозначим контекст как окружение, в рамках которого существует та или иная проблема.

Теперь самое интересное: решение проблемы может существовать либо в одном контексте, который вы уже рассматриваете, либо в другом.

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

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

Запомнить: через правильные вопросы мы помещаем свой мозг в нужные для решения задачи рамки.

Со мной происходит такое постоянно, но случалось ли с вами, что появился вопрос, который хотите задать коллегам, но пока вы его формулировали, то ответ находился самостоятельно?

@time2code
👍41
Блог готов! С чем пришлось справиться на старте?

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

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

Какая работа была проделана:
- Изучил большинство Hugo-тем, которые предлагаются для ведения блога. Остановился на PaperMod как самой приятной, хорошо работающей и активно поддерживаемой (это важно).
- Принял решение, что буду блог вести на двух языках (от этого зависела структура проекта).
- Так как решил вести блог на двух языках, то сразу выстрелил себе в ногу пришлось изучать множество нюансов, связанных с локализацией. На это потратил большую часть времени: тут и некорректное отображение даты на русском, странные заголовки, которые не хотели убираться из коробки, а также такие простые вещи, как скрытие текста и добавление плейсхолдера "Читать полностью...".

Финальным результатом я очень доволен. Особенно приятно, что создание блога было одной из моих целей на этот год.

Предлагаю ознакомиться 👈

В блоге доступны все свежие посты, начиная с сентября.

В планах (скорее всего, уйдет на 2025 год) добавить:
- Раздел с CV, где по одному клику HR смогут получить PDF.
- Теги, чтобы было удобнее группировать посты (+ облако тегов на главную или другой удобный инструмент для навигации).
- Поисковую строку (опционально, так как видел, что может не стабильно работать).
- Удобную пагинацию при большом количестве постов.

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

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

@time2code
👍11
Балансируя с производительностью. Можно ли жить без ORM?

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

Когда я писал на C#, то использовал Entity Framework Core - ORM для платформы .NET. А так как это был мой первый язык, то мне казалось это вполне естественным и об альтернативах я вообще не задумывался (возможно, на .NET тогда их и не было, не знаю есть ли сейчас).

В Go есть стандартная библиотека database/sql, представляющая собой общий интерфейс для работы с SQL-подобными базами. Если нужна максимальная производительность, то это ваш выбор.

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

Когда стандартного инструмента уже недостаточно, на помощь приходит sqlx, расширяя возможности, при сохранении производительности.

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

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

Подискутировав с командой и не найдя единого решения, я дошел до внутреннего Go-коммьюнити за советом. Коллеги единогласно разгромили ORM, и мы обсудили альтернативы 🤓

Из интересного мне приглянулась библиотека по генерации SQL-запросов - squirrel. И я решил сразу ее попробовать в бою.

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

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

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

Инициируем такой билдер, наполняя данными.

Важно не забыть выбрать плейсхолдер для переменных в зависимости от драйвера бд! Для Постгреса это доллар.


builder := squirrel.Insert("element").Columns("noscript", "price")

for _, element := range elements {
builder = builder.Values(
element.Title, element.Price)
}

query, args, err := builder.PlaceholderFormat(squirrel.Dollar).ToSql()
if err != nil {
return fmt.Errorf("failed to build query: %w", err)
}

_, err = tx.Exec(query, args...)
if err != nil {
return fmt.Errorf("failed to execute query: %w", err)
}


По итогу получаем нужный запрос:


INSERT INTO element (noscript, price) VALUES ($1, $2), ($3, $4)


Конечно, интересно еще посмотреть на различные тесты производительности кода с этой либой и без (возможно, сделаю в будущем бенчмарки), но пока выглядит отличным инструментом, когда вы переросли sqlx, а в полноценную ORM идти пока не хотите.

@time2code
👍3🔥2
Такой Fizz Buzz вы точно не видели!

Надеюсь, все знакомы с этой задачей, но если нет, то можно прочитать статью для контекста и попробовать себя на литкоде (задача уровня easy).

В феврале этого года я создал репозиторий fizz-buzz-world, который оформил так, чтобы в него можно было легко контрибьютить: написал несколько своих решений, добавил простых тестов на логику и организовал понятный ридми-файл с призывом добавлять свои реализации классической задачи.

Цель была простая - собирать нетривиальные решения Fizz Buzz на Go.

Например, вот несколько моих решений без единого if'а:


func (fb *FizzBuzzer) FizzBuzz() []string {
result := make([]string, 0, fb.n)

f := []string{"Fizz", "", ""}
b := []string{"Buzz", "", "", "", ""}

for i := 1; i < fb.n; i++ {
t := f[i%3] + b[i%5]
opts := []interface{}{i, t, t, t, t, t, t, t, t}
printed := fmt.Sprintf("%v", opts[len(t)])

result = append(result, printed)
}

return result
}


А вот еще одно:


func (fb *FizzBuzzer) FizzBuzz() []string {
result := make([]string, fb.n)

for i := 0; i < fb.n; i++ {
result[i] = strconv.Itoa(i + 1)
}

for i := 2; i < fb.n; i += 3 {
result[i] = "Fizz"
}

for i := 4; i < fb.n; i += 5 {
result[i] = "Buzz"
}

for i := 14; i < fb.n; i += 15 {
result[i] = "FizzBuzz"
}

return result
}


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

* * *

Шло время, а новых решений с момента создания репозитория так и не появилось. И вот спустя 8 месяцев на почту получаю уведомление, что в fizz-buzz-world прилетел первый пул-реквест!

Так неожиданно я стал опен-сорс разработчиком.

Хочу выразить признательность автору: ПР был отлично оформлен и задокументирован, а предлагаемое решение крайне неочевидное и очень интересное. Подробнее читайте в ридми от него.

В основе новой реализации лежит "счастливое число", которое подсчитано для данной задачи, и в Go версии 1.23.2 оно равно: 176064004.

Зная такое число, решение выглядит следующим образом:


func (fb *FizzBuzzer) FizzBuzz() []string {
result := make([]string, 0, fb.n)

predefined := []string{"", "Fizz", "Buzz", "FizzBuzz"}
var r *rand.Rand
for i := 1; i < fb.n; i++ {
if i%15 == 1 {
r = rand.New(rand.NewSource(LUCKY))
}
index := r.Int63() % 4
if index == 0 {
result = append(result, strconv.Itoa(i))
} else {
result = append(result, predefined[index])
}
}

return result
}


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

А еще, если у вас есть интересные идеи как можно решить Fizz Buzz, то предлагаю контрибьютить.

Если вы пишите на других языках, но знаете оригинальный метод решения, то пишите в личку, тред или создавайте issue - будем переводить ваше решение на Go и собирать самые безумные решения этой задачи вместе ;)

@time2code
4🔥2
Секрет счастливого числа в решении FizzBuzz.

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

На помощь приходит random seed, который обозначает число, на основании которого генератор всегда производит одну и ту же пвсевдослучайную последовательность.

В Go есть функция rand.NewSource. С ней мы можем задать нужный нам "seed". Это число и будет нашим "счастливым числом" (не путать с теорией чисел).

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


func izLucky() bool {
yep := []int{0, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 0, 0, 3}
for i := 0; i < 15; i++ {
r := int(r.Int63() % 4)
if yep[i] != r {
return false
}
}
return true
}


"yep" - массив, который содержит правильные индексы ответов первых 15 итераций. Если по указанному индексу выбрать строку из массива возможных вариантов: {"", Fizz, Buzz, FizzBuzz}, то мы получим корректное значение на выбранном шаге, а именно:


// ["", "", "Fizz", "", "Buzz", "Fizz", "", "", "Fizz", "Buzz", "", "Fizz", "", "", "FizzBuzz"]


То есть нам нужно подобрать такое число, при котором на каждой итерации для первых 15 элементов мы "случайно" будем получать правильный ответ, а вернее индекс, по которому сможет найти ответ.

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

Реинициализируем рандомайзер с заданным сидом каждые 15 итераций (внимание на "if i%15 == 1"):


func (fb *FizzBuzzer) FizzBuzz() []string {
// ...
var r *rand.Rand
for i := 1; i < fb.n; i++ {
if i%15 == 1 {
r = rand.New(rand.NewSource(176064004)) // <- lucky number
}
index := r.Int63() % 4
// ...
}

// ...
}


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

@time2code
1🔥1
Всегда ли автоматизация оправдана?

Когда был рядовым инженером в строительной компании, приходилось каждые 2 недели делать отчет на 150+ страниц в Power Point.

На это уходило много времени, и мне хотелось это автоматизировать.

К сожалению, мои знания языков программирования тогда стремились к нулю, но я уже понимал, что такое API и как с его помощью можно создавать полезные решения.

Но шло время и мне так и не удавалось автоматизировать презентацию, потому что это выглядело так:
1. Изучить программирование.
2. Написать скрипт для презентации.

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

Спустя год компания наняла PHP-программиста, который автоматизировал презентацию, но он не писал плагин на Power Point, а создал полноценное решение, из которого автоматически получался PDF-файл.

Это было круто и, конечно, мне захотелось также уметь ...

В информационной безопасности есть правило: затраты на защиту не должны превышать ценность защищаемого объекта.

Как не нужно на велосипед стоимостью 3 тысячи рублей вешать замок за 12, так и не стоит автоматизировать процесс, который руками выполняется за 2 часа при стоимости разработки автоматики в несколько месяцев.

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

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

Когда завел блог, то решил обязательно сделать удобный инструмент для добавления новых постов.

Первый месяц, пока занимался настройкой, то мне приходилось их создавать вручную, а так как я решил блог вести на двух языках, то добавление нового поста сводилось к следующему:
1. Найти правильную директорию (решил все хранить по принципу: год-месяц-статья). Если нет, например, нужного месяца, то создаем и уже туда помещаем файл для будущей статьи.
2. Вставить в шапку файла шаблон с метаинформацией.
3. Задать slug, который будет являться URL-путем.
4. Добавить содержание.
5. Повторить пункты 1-4 для английской статьи.

В какой-то момент я устал это делать и решил автоматизировать. Я знал, что hugo предлагает удобный способ создания нового поста на основе шаблона (что автоматически могло избавить меня от пункта 2).

Завел нужный шаблон архетипов, заодно организовав его таким образом, чтобы slug подставлялся из названия поста и теперь мои действия стали такими:
1. Команда в терминале:

hugo new 2024/september/mypost.md --kind post

2. Добавить содержание.
3. Повторить пункты 1-2 для английской статьи.

Это быстрее, чем было, но все равно медленно. И тут я вспоминаю про Makefile.

Бегу создавать его в корневой директории со следующим содержимым:


SLUG ?= "default-slug"
YEAR ?= $(shell date +"%Y")
MONTH ?= $(shell date +"%B" | tr '[:upper:]' '[:lower:]')

post:
hugo new posts/$(YEAR)/$(MONTH)/$(SLUG).md --kind post_en && \
hugo new ../ru/posts/$(YEAR)/$(MONTH)/$(SLUG).md --kind post_ru


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

После чего изначальный труд свелся к следующим действиям:
1. Команда в терминале:

make post SLUG="mypost"

2. Добавить содержание.
3. Повторить пункт 2 для английской статьи.

Очевидно, можно и дальше продолжить.

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

Но это уже другая история и текущая автоматизация меня полностью всем устраивает.

@time2code
👍4
В поисках первопричины. Как найти точку отказа?

Это история произошла со мной недавно и потребовала немало ресурсов. Хочется порефлексировать и поделиться опытом, который может оказаться полезным для всех, кто ежедневно сталкивается с поиском и устранением проблем на проде.

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


Пролог

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

Например, если говорят, что SLA = 4 часа, то это означает, что в течение четырех часов с момента обращения на ваш запрос должны отреагировать.

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

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

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

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

Удивительно, но подобные сценарии были настолько редкими, что продуктовые метрики никак не фиксировали проблему и мне пришлось серьезно заняться расследованием.

Продолжение следует...

@time2code
👍1
В поисках первопричины. Как найти точку отказа?

Ранее:
- Пролог

Часть I - Обращения продолжают поступать

1. Первым делом иду в историю коммитов сервиса, горизонтально отвечающего за данный сценарий. Вижу много релизов и начинаю проверять каждый, начиная от момента, когда появилось первое обращение о проблеме. Пролистав порядка 10 релизов и не найдя ничего подозрительного в истории изменений, двигаюсь дальше.
2. Иду смотреть дашборды сервиса. Смотрю технические и продуктовые метрики по разным сценариям в надежде увидеть какие-то отклонения от нормального состояния, но тщетно.
3. Следующая остановка Sentry и Kibana. В обилии логов и ошибок нахожу подходящую под мой случай. Текст ошибки указывает на проблему в другом горизонтальном сервисе.
4. Обсуждаем эту историю с QA и находим название "проблемного сервиса".
5. Повторяются пункты 1-3 для найденного сервиса. По коммитам последних релизов снова проблема неочевидна, но вот на дашбордах можно заметить интересное: нужная RPC-ручка время от времени не справляется с нагрузкой и начинает отдавать ошибки.
6. Обрадовавшись, что проблема найдена, идем в чат поддержки данного сервиса и указываем на проблему.
7. Спустя какое-то время нам подтверждают, что о ситуации известно: виноват Redis, а в следующем спринте запланирована починка.
8. В этот момент хотелось расслабиться и ждать, пока ребята все наладят, но обращения по проблеме от пользователей не прекращают поступать, и вот мы уже повышаем приоритет бага до "серьезного". Чинить нужно срочно, нет времени ждать следующего спринта.

Продолжение следует...

@time2code
👍1
В поисках первопричины. Как найти точку отказа?

Ранее:
- Пролог
- Часть I - Обращения продолжают поступать

Часть II - Удивительное совпадение

9. Решив еще раз сравнить графики ошибок двух сервисов, я вижу несоответствие. Проблемный сервис действительно работает с перебоями, но почему-то моменты его отказа не полностью коррелируют с фоном ошибок у вызывающего сервиса, а значит, что должно быть что-то еще, что оказывает негативное влияние.
10. Иду к ребятам из горизонтальной команды, чтобы помогли установить причину, погрузившись своей экспертизой.
11. Локализовав проблему, коллеги подсказывают, что не формируется диплинк, который должен отдавать уже третий сервис и, если они его не получают, то может происходить похожее поведение. У меня появляется серьезная надежда, что первопричина найдена.
12. Узнав какой сервис формирует диплинк, иду по классической схеме из п.1-3 и вижу... Накануне дня, когда начались проблемы, был совершен коммит, задевающий логику по формированию нужного нам диплинка и при этом авторы - коллеги из моей вертикали, что как будто сигнализирует о том, что вот она разгадка.
13. Иду к ребятам и прошу посмотреть нашу ситуацию и спроецировать на свой код: могли ли они что-то задеть?
14. После некоторого анализа и череды сообщений в разных тредах, к моему удивлению, это оказалось удивительным совпадением. Функционал задели не они...

Продолжение следует...

@time2code
👍1
В поисках первопричины. Как найти точку отказа?

Ранее:
- Пролог
- Часть I - Обращения продолжают поступать
- Часть II - Удивительное совпадение

Часть III - Пятничный деплой

15. На этом этапе уже отчаявшись, я поднимаю 3+ сервиса локально и начинаю детальный дебаг с проблемным запросом, который мне любезно подготовил QA (спасибо ему большое, одному было бы гораздо дольше с этим разбираться). Я решил идти построчно по коду, начиная с самого первого сервиса.
16. Спустя некоторое время, неожиданно наблюдаю, что запрос, пройдя множество сервисов и вернувшись в первый, прерывается в странном месте. В конструкции switch-case ни один case не срабатывает, запрос уходит в default, из-за чего и возвращается непонятная ошибка.
17. Показав это поведение ответственному за горизонтальный сервис, он сразу подтвердил, что вот он корень проблемы, должна быть другая логика.
18. Пытаюсь понять как исправить. Быстрый анализ конструкции показывает, что сравнивались различные типы ошибок и в зависимости от этого выполнялась дальнейшая обработка. В нашем случае тип ошибки перестал определяться, из-за чего сервис не понимал как ее обрабатывать. Но почему так начало происходить?
19. Начинаю искать, где формируется ошибка, и вижу: добавился враппер, который обернул нашу ошибку и добавил дополнительный контекст... А так как в Go работа с ошибками построена максимально примитивно, то легко можно ошибиться - это здесь и произошло.
20. Готовлю "быстрый" фикс, QA тестирует и подтверждает, что проблема, с которой пришли пользователи, ушла!
21. Классический пятничный деплой в конце спринта и томительное ожидание за просмотром метрик, где раньше наблюдались ошибки...
22. Фон ошибок резко падает. На графиках видно, как нормализуются ситуация. Наконец, можно выдохнуть. Проблема решена, я спокойно ухожу на выходные, а пользователи получают возможность завершить свой обычный сценарий.

Эпилог следует...

@time2code
👍2