Паттерн early exit
Какие задачи стоят перед программистом, когда он реализует какую-то функцию? Обеспечить корректность алгоритма, при минимальной сложности кода. Именно в этом помогает паттерн early exit.
📍 В чём суть: когда в вашем алгоритме появляется оператор ветвления (
ℹ️ Пример:
- Без early exit
- То же самое, но с early exit:
У early exit подхода есть ряд преимуществ:
1️⃣ Снижается вложенность операторов - это упрощает чтение кода.
2️⃣ Повышается фокус на основной путь выполнения функции - часто, когда дочитываешь длинную (вероятно, главную) ветвь выполнения кода, уже забываешь, зачем выше было ветвление и в чём смысл короткой ветви. Early exit позволяет быстро прочитать короткую ветвь и забыть о ней, сфокусировавшись на длинной ветви. В итоге, программисту нужно меньше “оперативной памяти” чтобы прочитать код 😊
3️⃣ Повышается корректность алгоритма - все граничные условия можно рассмотреть в самом начале функции, по моему опыту такой подход снижает количество ошибок.
4️⃣ Если в вашем языке нет
^ такой подход особенно популярен в ядре Linux: раз, два, три. Думаю, авторы этого кода что-то знают о программировании 😉
💡 Итого: при использовании операторов ветвления короткие ветви алгоритма следует обрабатывать перед длинными. Этот подход называется early exit. Он позволяет писать более простой и корректный код.
Ставь огонёк, если используешь early exit в своей работе 🔥
#hardskills #coding #pattern #bestpractice #codereading
Какие задачи стоят перед программистом, когда он реализует какую-то функцию? Обеспечить корректность алгоритма, при минимальной сложности кода. Именно в этом помогает паттерн early exit.
📍 В чём суть: когда в вашем алгоритме появляется оператор ветвления (
if
, switch
и т.п.), короткую ветвь вычислений надо обработать перед длинной ветвью.ℹ️ Пример:
- Без early exit
if user.isAuthorized() {
// Do
// some
// business
// logic
} else {
return errors.New("403 Anauthorized")
}
- То же самое, но с early exit:
if !user.isAuthorized() {
return errors.New("403 Anauthorized")
}
// Do
// some
// business
// logic
У early exit подхода есть ряд преимуществ:
1️⃣ Снижается вложенность операторов - это упрощает чтение кода.
2️⃣ Повышается фокус на основной путь выполнения функции - часто, когда дочитываешь длинную (вероятно, главную) ветвь выполнения кода, уже забываешь, зачем выше было ветвление и в чём смысл короткой ветви. Early exit позволяет быстро прочитать короткую ветвь и забыть о ней, сфокусировавшись на длинной ветви. В итоге, программисту нужно меньше “оперативной памяти” чтобы прочитать код 😊
3️⃣ Повышается корректность алгоритма - все граничные условия можно рассмотреть в самом начале функции, по моему опыту такой подход снижает количество ошибок.
4️⃣ Если в вашем языке нет
defer
'ов и сборки мусора, разновидность early exit упрощает корректную деаллокацию ресурсов. Пример:void foo() {
void *a = malloc(1024);
if (!a)
goto out_a;
void *b = malloc(1024);
if (!b)
goto out_b;
void *c = malloc(1024);
if (!c)
goto out_c;
/*
* Метки позволяют деаллоцировать только те ресурсы,
* которые были успешно аллоцированы
*/
free(c);
out_c:
free(b);
out_b:
free(a);
out_a:
return;
}
^ такой подход особенно популярен в ядре Linux: раз, два, три. Думаю, авторы этого кода что-то знают о программировании 😉
💡 Итого: при использовании операторов ветвления короткие ветви алгоритма следует обрабатывать перед длинными. Этот подход называется early exit. Он позволяет писать более простой и корректный код.
Ставь огонёк, если используешь early exit в своей работе 🔥
#hardskills #coding #pattern #bestpractice #codereading
Анализируем файловые дескрипторы lsof
Про подсистему ввода/вывода поговорили, а как это дебажить опять никто не сказал 🤡 Давайте исправлять!
📂 Когда мы открываем файл (пайп, сетевое соединение, и т.д.), операционная система даёт нам файловый дескриптор - это неотрицательное число, которое идентифицирует открытый файл. В дальнейшем, при каждой операции над этим файлом мы будем передавать файловый дескриптор, чтобы ОС могла понять, над каким файлом производится операция.
⚙️ В пространстве ядра у каждого процесса есть таблица открытых файлов. Файловый дескриптор - это по сути номер строчки в этой таблице, т.е. ключ. По ключу хранится значение - контекст открытого файла. В этой структуре хранятся, например:
- позиция чтения/записи (это похоже на положение курсора в тексте);
- флаги, с которыми открыт файл (только для чтения/для чтения и записи, блокирующий/неблокирующий режим и т.п.);
- примитивы для блокировок;
- и прочие переменные, необходимые для операций над открытыми файлами.
🔧 К чему я всё это написал: в UNIX есть замечательная утилита - lsof. Она работает с таблицей открытых файлов и может помочь в отладке операций ввода/вывода. Разберём несколько рецептов:
1️⃣ Вывести таблицу открытых файлов процесса:
☝️ Настоятельно рекомендую выполнить эту команду прямо сейчас, если вы первый раз слышите об lsof. Там куча всего интересного:
- Список открытых файлов на диске - логи, рабочая директория процесса,
- Список подгруженных разделяемых библиотек
- Список установленных сетевых соединений (btw, lsof может быть реальной альтернативой netstat, при чём под MacOS по моим наблюдениям lsof работает так же, как и под Linux)
В общем, lsof даёт много полезной инфы.
2️⃣ Вывести список процессов, работающих с файлом:
🤔 С какими проблемами может помочь lsof?
✅ Проверка доступа к файлам и сетевым ресурсам. Когда процесс “должен куда-то ходить” или наоборот “не должен куда-то ходить”, lsof подскажет - пытается он или нет.
✅ Отладка ошибки
✅ Отладка разделяемых библиотек - lsof покажет, какие либы процесс подгрузил, чтобы программист смог провалидировать, всё ли в этом списке в рамках ожиданий.
💎 В общем, lsof - невероятно полезная утилита. Меня самого она несколько раз очень сильно выручала. Надеюсь, теперь она будет полезна и вам 😉
Ставь огонёк, если lsof - топ 🔥
#hardskills #tools #Linux #fs #networks #debug
Про подсистему ввода/вывода поговорили, а как это дебажить опять никто не сказал 🤡 Давайте исправлять!
📂 Когда мы открываем файл (пайп, сетевое соединение, и т.д.), операционная система даёт нам файловый дескриптор - это неотрицательное число, которое идентифицирует открытый файл. В дальнейшем, при каждой операции над этим файлом мы будем передавать файловый дескриптор, чтобы ОС могла понять, над каким файлом производится операция.
⚙️ В пространстве ядра у каждого процесса есть таблица открытых файлов. Файловый дескриптор - это по сути номер строчки в этой таблице, т.е. ключ. По ключу хранится значение - контекст открытого файла. В этой структуре хранятся, например:
- позиция чтения/записи (это похоже на положение курсора в тексте);
- флаги, с которыми открыт файл (только для чтения/для чтения и записи, блокирующий/неблокирующий режим и т.п.);
- примитивы для блокировок;
- и прочие переменные, необходимые для операций над открытыми файлами.
🔧 К чему я всё это написал: в UNIX есть замечательная утилита - lsof. Она работает с таблицей открытых файлов и может помочь в отладке операций ввода/вывода. Разберём несколько рецептов:
1️⃣ Вывести таблицу открытых файлов процесса:
lsof -p "<PID>"
☝️ Настоятельно рекомендую выполнить эту команду прямо сейчас, если вы первый раз слышите об lsof. Там куча всего интересного:
- Список открытых файлов на диске - логи, рабочая директория процесса,
/dev/null
и т.п.- Список подгруженных разделяемых библиотек
- Список установленных сетевых соединений (btw, lsof может быть реальной альтернативой netstat, при чём под MacOS по моим наблюдениям lsof работает так же, как и под Linux)
В общем, lsof даёт много полезной инфы.
2️⃣ Вывести список процессов, работающих с файлом:
lsof "/path/to/file"
🤔 С какими проблемами может помочь lsof?
✅ Проверка доступа к файлам и сетевым ресурсам. Когда процесс “должен куда-то ходить” или наоборот “не должен куда-то ходить”, lsof подскажет - пытается он или нет.
✅ Отладка ошибки
Too many open files
- размер таблицы открытых файлов ограничен, из-за чего при открытии нового файла (или установке соединения) может возникать такая ошибка. lsof подскажет, имеет ли место утечка файловых дескрипторов (это когда программист close
забыл позвать) и какие именно ФД утекли. Или наоборот, при анализе lsof можно провалидировать, что всё ок, и нужно просто поднять лимит.✅ Отладка разделяемых библиотек - lsof покажет, какие либы процесс подгрузил, чтобы программист смог провалидировать, всё ли в этом списке в рамках ожиданий.
💎 В общем, lsof - невероятно полезная утилита. Меня самого она несколько раз очень сильно выручала. Надеюсь, теперь она будет полезна и вам 😉
Ставь огонёк, если lsof - топ 🔥
#hardskills #tools #Linux #fs #networks #debug
Что под капотом у файловых систем?
Не спросил меня ни разу ни один человек в мире 😂 А тема то на самом деле интересная! И раз уж мне в процессе работы в VK довелось написать свою файловую систему, я проявлю инициативу и напишу об этом сюда.
🗂 Итак, файловая система - это ПО для организации файлов и работы с ними. Чаще всего (хотя и не всегда), файловая система является модулем ядра ОС. Придумали ФС прежде всего для того, чтобы спрятать сложные низкоуровневые API жёстких дисков за простым и понятным для пользователя интерфейсом.
🌲 Файлы под управлением ФС в Linux объединяются в дерево: директории (папки) могут быть вложены одна в другую, листьями дерева являются обычные файлы. Как-то так:
📑 На одном Linux-хосте одновременно могут эксплуатироваться десятки файловых систем. Выполните команду
🧩 Почему дерево файлов с точки зрения пользователя выглядит однородным, если за разные его части отвечают разные ФС? Дело в том, что все ФС в Linux реализуют общий интерфейс. Например, все ФС должны уметь
⚙️ И вот теперь мы готовы заглянуть под капот. Как написать свою файловую систему? Если кратко, то надо написать программу, которая будет соответствовать интерфейсу vfs. Чтобы требованиям интерфейса было проще соответствовать, внутри ФС лучше будет оперировать теми же сущностями, что и ФС общего назначения. Давайте рассмотрим их подробнее:
- имя файла (строка) - человеко-читаемый индентификатор файла;
- инода (id) - по сути указатель на блок данных с содержимым файла;
- родительская инода (id) - инода директории, в которой находится файл.
Зачем было разделять файлы на 2 сущности: dentry и inode? Оптимизация! Например, вы можете копировать файл между директориями без фактической перезаписи данных (inode) на диске - вам нужно просто поменять dentry.
Σ Совокупность этих 3-ёх структур данных образует файл. Забавно получается, в файловой системе отдельной сущности “файл” как раз и нет 😊
🤔 Зачем вам это знать? Не думаю, что вам придётся писать свою ФС - задача довольно редкая. Однако, при разработке stateful приложений или системном администрировании бывает полезно знать, как ФС устроены изнутри. Внутрянка ФС там иногда вылезает наружу: например, иноды могут закончиться 0_o
✨ А ещё это классный кейс хорошего дизайна систем: разделение данных с разным паттерном доступа на отдельные сущности может обеспечить бОльшую гибкость в работе с этими данными и позволить оптимизировать хранение.
💎 И напоследок хотел бы обратить внимание на vfs - хрестоматийный пример отделения интерфейса от реализации, что позволило обеспечить расширяемость и упростить эволюцию системы.
Ставьте огонёк, если вам интересно читать про внутреннее устройство разных систем 🔥
#theory #Linux #fs
Не спросил меня ни разу ни один человек в мире 😂 А тема то на самом деле интересная! И раз уж мне в процессе работы в VK довелось написать свою файловую систему, я проявлю инициативу и напишу об этом сюда.
🗂 Итак, файловая система - это ПО для организации файлов и работы с ними. Чаще всего (хотя и не всегда), файловая система является модулем ядра ОС. Придумали ФС прежде всего для того, чтобы спрятать сложные низкоуровневые API жёстких дисков за простым и понятным для пользователя интерфейсом.
🌲 Файлы под управлением ФС в Linux объединяются в дерево: директории (папки) могут быть вложены одна в другую, листьями дерева являются обычные файлы. Как-то так:
# Утилита tree в Linux позволяет вывести поддерево файлов
tree ./test
./test
|-- dir1
| `-- hello_world.txt
`-- dir2
2 directories, 1 file
📑 На одном Linux-хосте одновременно могут эксплуатироваться десятки файловых систем. Выполните команду
findmnt
, если не верите 😉 При этом каждый инстанс файловой системы отвечает за своё поддерево файлов. Процесс добавления файловой системы в дерево файлов называется монтирование, его можно выполнить вот так: mount <filesystem id> </path/to/directory>
🧩 Почему дерево файлов с точки зрения пользователя выглядит однородным, если за разные его части отвечают разные ФС? Дело в том, что все ФС в Linux реализуют общий интерфейс. Например, все ФС должны уметь
open
, close
, read
, write
и т.д. За общий интерфейс для всех файловых систем в Linux отвечает подсистема vfs.⚙️ И вот теперь мы готовы заглянуть под капот. Как написать свою файловую систему? Если кратко, то надо написать программу, которая будет соответствовать интерфейсу vfs. Чтобы требованиям интерфейса было проще соответствовать, внутри ФС лучше будет оперировать теми же сущностями, что и ФС общего назначения. Давайте рассмотрим их подробнее:
1️⃣ inode
- блок данных с целочисленным id. Фактически хранит данные, т.е. знает, где именно (по какому адресу) они расположены на диске.2️⃣ dentry
- узел в дереве файлов. У дентри есть три главных поля:- имя файла (строка) - человеко-читаемый индентификатор файла;
- инода (id) - по сути указатель на блок данных с содержимым файла;
- родительская инода (id) - инода директории, в которой находится файл.
Зачем было разделять файлы на 2 сущности: dentry и inode? Оптимизация! Например, вы можете копировать файл между директориями без фактической перезаписи данных (inode) на диске - вам нужно просто поменять dentry.
3️⃣ struct file
- контекст открытого файла, отвечает за блокировки, позицию чтения/записи в файл, контроль доступа к файлу и т.п.Σ Совокупность этих 3-ёх структур данных образует файл. Забавно получается, в файловой системе отдельной сущности “файл” как раз и нет 😊
🤔 Зачем вам это знать? Не думаю, что вам придётся писать свою ФС - задача довольно редкая. Однако, при разработке stateful приложений или системном администрировании бывает полезно знать, как ФС устроены изнутри. Внутрянка ФС там иногда вылезает наружу: например, иноды могут закончиться 0_o
✨ А ещё это классный кейс хорошего дизайна систем: разделение данных с разным паттерном доступа на отдельные сущности может обеспечить бОльшую гибкость в работе с этими данными и позволить оптимизировать хранение.
💎 И напоследок хотел бы обратить внимание на vfs - хрестоматийный пример отделения интерфейса от реализации, что позволило обеспечить расширяемость и упростить эволюцию системы.
Ставьте огонёк, если вам интересно читать про внутреннее устройство разных систем 🔥
#theory #Linux #fs
Классификация ПО: stateful vs stateless
Умение классифицировать ПО - важный хард скил профессионального программиста. Он позволяет быстрее разбираться с тем, как устроен незнакомый софт и чего можно ждать от его эксплуатации.
👋 Сегодня рассмотрим 2 больших класса ПО: stateful и stateless.
☁️ Stateless - ПО без хранения состояния. Если вашей программе не нужно ничего хранить на диске, то она stateless. Самый простой пример stateless приложения - калькулятор. С одними и теми же входными данными калькулятор всегда вернёт одинаковый результат. Ничего сохранять не нужно - всё можно быстро рассчитать заново.
🪨 Stateful - ПО c хранением состояния. Состояние - это те данные, которые мы хотим сохранить надолго. Они не должны потеряться при перезапуске программы или компьютера (такие “важные” данные называются персистентными, анг. persistent). Stateful приложения сохраняют данные на жёсткий диск. Самый простой пример stateful приложения - заметки. Пользователь открывает заметки исключительно ради сохранения данных, никаких сложных алгоритмов там нет.
🔗 Рассмотрим свойства этих классов ПО:
1️⃣ Stateless приложения проще масштабировать - можно просто поднять несколько копий приложения и балансировать нагрузку между ними. Со stateful так не получится: когда данных становится слишком много (настолько, что на жёсткий диск не влезает), придётся как-то разделять данные между копиями приложения. При чём каждая копия будет обслуживать свою уникальную (!) часть общих данных. Этот процесс называется шардирование.
2️⃣ Stateless приложения проще запустить в облаке. В облачном окружении копии приложения периодически завершаются и запускаются, при этом возможен автоматический переезд приложения с одного сервера на другой. Со stateful приложениями это так просто не прокатит - недостаточно запустить приложение на другом сервере, надо ещё все его данные перевезти. А это совсем непростая задача 😅
3️⃣ Для stateful приложений надо заботиться о сохранности данных. Резервное копирование, хранение нескольких копий в разных ЦОД, проверки целостности данных (например, с помощью контрольных сумм) - это всё огромный пласт работ, которые надо выполнить для нормального функционирования приложения.
4️⃣ Stateless приложениям проще эволюционировать. В реальных продуктах важно позаботиться о том, как вы будете поставлять новые версии приложения. И как откатывать багованные версии - тоже 😉 Это сама по себе непростая задача, а для stateful приложений она становится вдвойне сложнее. Как обновить формат хранимых данных? А как откатить обновление? А что, если какие-то данные перед откатом успели записаться в новом формате? На все эти вопросы придётся искать ответ 😐
🖼 Со стороны может показаться, что разделение stateful/stateless - это исключительно бэкендерская тема. Однако, в клиентской разработке оно тоже имеет место. Например, в мобильной разработке часто сохраняют на диск устройства какие-то кеши, пользовательские настройки или черновики. В современном web-фронтенде тоже куча stateful-апишек браузера есть: Cookies, Local Storage, Session Storage, Web SQL, File System Access API и наверняка много чего ещё.
🍋 К этому моменту читатель мог бы придти к выводу, что разработка stateful приложения - это какой-то ужас, надо отказаться от стейта и жизнь станет легче. В целом, это не самый плохой вывод. Если вы можете обеспечить классный пользовательский опыт без хранения данных, то так и делайте. Ваше приложение может работать без регистрации? Супер, в топку её!
🛢 Однако, не просто так говорят, что “данные - новая нефть”. К сожалению, чаще всего без данных обойтись не получается. Что делать в этом случае? Отделяйте слой хранения данных от вычислений (логики приложения). В этом вам помогут разного рода СУБД и файловые серверы (типа S3), они уже умеют решать многие проблемы.
Ставьте огонёк если классификация stateful/stateless для вас полезна 🔥А какое приложение вы сейчас разрабатываете? Напишите в комменты 👇
#theory #architecture
Умение классифицировать ПО - важный хард скил профессионального программиста. Он позволяет быстрее разбираться с тем, как устроен незнакомый софт и чего можно ждать от его эксплуатации.
👋 Сегодня рассмотрим 2 больших класса ПО: stateful и stateless.
☁️ Stateless - ПО без хранения состояния. Если вашей программе не нужно ничего хранить на диске, то она stateless. Самый простой пример stateless приложения - калькулятор. С одними и теми же входными данными калькулятор всегда вернёт одинаковый результат. Ничего сохранять не нужно - всё можно быстро рассчитать заново.
🪨 Stateful - ПО c хранением состояния. Состояние - это те данные, которые мы хотим сохранить надолго. Они не должны потеряться при перезапуске программы или компьютера (такие “важные” данные называются персистентными, анг. persistent). Stateful приложения сохраняют данные на жёсткий диск. Самый простой пример stateful приложения - заметки. Пользователь открывает заметки исключительно ради сохранения данных, никаких сложных алгоритмов там нет.
🔗 Рассмотрим свойства этих классов ПО:
1️⃣ Stateless приложения проще масштабировать - можно просто поднять несколько копий приложения и балансировать нагрузку между ними. Со stateful так не получится: когда данных становится слишком много (настолько, что на жёсткий диск не влезает), придётся как-то разделять данные между копиями приложения. При чём каждая копия будет обслуживать свою уникальную (!) часть общих данных. Этот процесс называется шардирование.
2️⃣ Stateless приложения проще запустить в облаке. В облачном окружении копии приложения периодически завершаются и запускаются, при этом возможен автоматический переезд приложения с одного сервера на другой. Со stateful приложениями это так просто не прокатит - недостаточно запустить приложение на другом сервере, надо ещё все его данные перевезти. А это совсем непростая задача 😅
3️⃣ Для stateful приложений надо заботиться о сохранности данных. Резервное копирование, хранение нескольких копий в разных ЦОД, проверки целостности данных (например, с помощью контрольных сумм) - это всё огромный пласт работ, которые надо выполнить для нормального функционирования приложения.
4️⃣ Stateless приложениям проще эволюционировать. В реальных продуктах важно позаботиться о том, как вы будете поставлять новые версии приложения. И как откатывать багованные версии - тоже 😉 Это сама по себе непростая задача, а для stateful приложений она становится вдвойне сложнее. Как обновить формат хранимых данных? А как откатить обновление? А что, если какие-то данные перед откатом успели записаться в новом формате? На все эти вопросы придётся искать ответ 😐
🖼 Со стороны может показаться, что разделение stateful/stateless - это исключительно бэкендерская тема. Однако, в клиентской разработке оно тоже имеет место. Например, в мобильной разработке часто сохраняют на диск устройства какие-то кеши, пользовательские настройки или черновики. В современном web-фронтенде тоже куча stateful-апишек браузера есть: Cookies, Local Storage, Session Storage, Web SQL, File System Access API и наверняка много чего ещё.
🍋 К этому моменту читатель мог бы придти к выводу, что разработка stateful приложения - это какой-то ужас, надо отказаться от стейта и жизнь станет легче. В целом, это не самый плохой вывод. Если вы можете обеспечить классный пользовательский опыт без хранения данных, то так и делайте. Ваше приложение может работать без регистрации? Супер, в топку её!
🛢 Однако, не просто так говорят, что “данные - новая нефть”. К сожалению, чаще всего без данных обойтись не получается. Что делать в этом случае? Отделяйте слой хранения данных от вычислений (логики приложения). В этом вам помогут разного рода СУБД и файловые серверы (типа S3), они уже умеют решать многие проблемы.
Ставьте огонёк если классификация stateful/stateless для вас полезна 🔥А какое приложение вы сейчас разрабатываете? Напишите в комменты 👇
#theory #architecture
Масштабирование: вертикальное vs горизонтальное
Если ваше приложение успешно, то с ростом количества пользователей вы рано или поздно упрётесь в потолок производительности, которую могут обеспечить ваши серверы. При приближении к этому потолку крайне остро встаёт вопрос масштабирования - увеличения доступных вычислительных мощностей.
📈 Есть 2 подхода к масштабированию бэкенда:
1️⃣ Вертикальное масштабирование - выкидваем старый сервер, покупаем сервер по-мощнее ему на замену. Это если грубо 😉 Но суть именно такая: при вертикальном масштабировании увеличивают количество вычислительных ресурсов, доступных на одной машине.
2️⃣ Горизонтальное масштабирование - оставляем старый сервер как есть, покупаем второй такой же. Поднимаем вторую копию приложения, распределяем нагрузку между ними.
🧐 В чём разница между двумя подходами?
↕️ Вертикальное масштабирование технически выполнить проще, поэтому долгое время компании предпочитали именно этот подход. Однако с ним есть одна большая проблема: стоимость такого масштабирования растёт нелинейно. В итоге, на определённом этапе вертикальное масштабирование становится неприемлемо дорогим.
↔️ Горизонтальное масштабирование, напротив, выполнить труднее - с ним куча маленьких проблем. Надо администрировать больше серверов, заботиться о балансировке нагрузки, больше компонентов аппаратного обеспечения закупать в конце концов. Однако, из-за того, что аппаратное обеспечение при горизонтальном масштабировании используется типовое, производящееся крупными партиями, экономика начинает сходиться лучше. При таком подходе рост стоимости эксплуатации практически линейный. Из-за лучших экономических характеристик, последние лет 10 горизонтальное масштабирование - де-факто стандарт индустрии.
💡 Какие отсюда можно сделать выводы? Позаботьтесь о масштабируемости системы на этапе проектирования. Задайте себе вопрос: сможете ли вы поднять вторую копию приложения, когда это станет необходимо? Что нужно сделать, чтобы это не составило больших проблем?
🔥 В отличие от серверных систем, этот канал масштабируется исключительно вертикально :) Делитесь контентом с друзьями и коллегами - посмотрим, какую нагрузку мы можем выдержать 😊
#theory #highload #architecture
Если ваше приложение успешно, то с ростом количества пользователей вы рано или поздно упрётесь в потолок производительности, которую могут обеспечить ваши серверы. При приближении к этому потолку крайне остро встаёт вопрос масштабирования - увеличения доступных вычислительных мощностей.
📈 Есть 2 подхода к масштабированию бэкенда:
1️⃣ Вертикальное масштабирование - выкидваем старый сервер, покупаем сервер по-мощнее ему на замену. Это если грубо 😉 Но суть именно такая: при вертикальном масштабировании увеличивают количество вычислительных ресурсов, доступных на одной машине.
2️⃣ Горизонтальное масштабирование - оставляем старый сервер как есть, покупаем второй такой же. Поднимаем вторую копию приложения, распределяем нагрузку между ними.
🧐 В чём разница между двумя подходами?
↕️ Вертикальное масштабирование технически выполнить проще, поэтому долгое время компании предпочитали именно этот подход. Однако с ним есть одна большая проблема: стоимость такого масштабирования растёт нелинейно. В итоге, на определённом этапе вертикальное масштабирование становится неприемлемо дорогим.
↔️ Горизонтальное масштабирование, напротив, выполнить труднее - с ним куча маленьких проблем. Надо администрировать больше серверов, заботиться о балансировке нагрузки, больше компонентов аппаратного обеспечения закупать в конце концов. Однако, из-за того, что аппаратное обеспечение при горизонтальном масштабировании используется типовое, производящееся крупными партиями, экономика начинает сходиться лучше. При таком подходе рост стоимости эксплуатации практически линейный. Из-за лучших экономических характеристик, последние лет 10 горизонтальное масштабирование - де-факто стандарт индустрии.
💡 Какие отсюда можно сделать выводы? Позаботьтесь о масштабируемости системы на этапе проектирования. Задайте себе вопрос: сможете ли вы поднять вторую копию приложения, когда это станет необходимо? Что нужно сделать, чтобы это не составило больших проблем?
🔥 В отличие от серверных систем, этот канал масштабируется исключительно вертикально :) Делитесь контентом с друзьями и коллегами - посмотрим, какую нагрузку мы можем выдержать 😊
#theory #highload #architecture
Docker - контейнеризация, доступная всем
Контейнеры - самый современный и один из наиболее популярных способов поставки backend-приложений. Как работают контейнеры я уже писал, пора научиться ими пользоваться.
🐳 Docker - одна из причин популярности контейнеров сегодня. Это самый популярный контейнерный рантайм на сегодняшний день. Авторы этого ПО смогли спрятать всю сложность внутренней реализации контейнеров за очень простым интерфейсом. В результате, получилась очень мощная технология, которая быстро завоевала аудиторию среди разработчиков.
📥 Перед началом работы необходимо установить docker. В стандартную поставку docker входят cli-утилита и docker-демон. После установки надо удостовериться, что демон запущен:
⚙️ Теперь, когда окружение готово, попробуем запустить чего-нибудь в контейнере. Создадим файлик
📃 Далее нам нужен имейдж - неизменяемый архив, в который мы запакуем окружение для нашего приложения. Для сборки имейджа докеру нужна инструкция. Создадим файлик
^ больше информации про Dockerfile в официальной документации.
🔨 Собираем имейдж из Dockerfile:
👟 Запускаем контейнер (т.е. стартуем процесс нашего приложения):
Если вы всё сделали правильно, на этом этапе увидите
♻️ Приберём за собой и почистим созданные ранее ресурсы:
👍 На сегодня знакомство с docker закончим. Напоминаю, что контейнеры - это очень полезная технология, на её изучение имеет смысл потратить время 😉 Ставь огонёк, если хочешь более продвинутые туториалы по docker 🔥
#hardskills #containers #tools #Linux #docker
Контейнеры - самый современный и один из наиболее популярных способов поставки backend-приложений. Как работают контейнеры я уже писал, пора научиться ими пользоваться.
🐳 Docker - одна из причин популярности контейнеров сегодня. Это самый популярный контейнерный рантайм на сегодняшний день. Авторы этого ПО смогли спрятать всю сложность внутренней реализации контейнеров за очень простым интерфейсом. В результате, получилась очень мощная технология, которая быстро завоевала аудиторию среди разработчиков.
📥 Перед началом работы необходимо установить docker. В стандартную поставку docker входят cli-утилита и docker-демон. После установки надо удостовериться, что демон запущен:
docker stats --no-stream
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
# Ошибка говорит о том, что демон не запущен
# В зависимости от способа установки, надо:
# - либо запустить либо GUI-приложение Docker Desktop
# - либо запустить docker-демон через systemctl
systemctl start docker
docker stats --no-stream
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
⚙️ Теперь, когда окружение готово, попробуем запустить чего-нибудь в контейнере. Создадим файлик
helloworld.py
, это наше тестовое приложение. Содержимое файла:print('Hello world')
📃 Далее нам нужен имейдж - неизменяемый архив, в который мы запакуем окружение для нашего приложения. Для сборки имейджа докеру нужна инструкция. Создадим файлик
Dockerfile
, в нём мы опишем, какие шаги надо выполнить для сборки имейджа. Содержимое файла:# Указываем базовый имейдж.
# Нам не обязательно создавать свой имейдж с нуля, можно взять за основу что-то готовое.
# Обычно, за основу берут базовый имейдж с желаемым дистрибутивом Linux и основным ПО.
# В данном случае, дистрибутив нам не важен. Нас устроит любой, где есть python версии 3.
# Имейджи по-умолчанию скачиваются с https://hub.docker.com/
FROM python:3
# Добавляем наше приложение в имейдж
ADD helloworld.py /root/helloworld.py
# По-умолчанию при запуске контейнера из нашего имейджа будет выполнена вот эта команда.
CMD ["python3", "/root/helloworld.py"]
^ больше информации про Dockerfile в официальной документации.
🔨 Собираем имейдж из Dockerfile:
docker build --tag helloworld .
# docker build - собрать имедйж
# --tag helloworld - назвать имейдж "helloworld"
# . - Dockerfile искать в текущей директории
👟 Запускаем контейнер (т.е. стартуем процесс нашего приложения):
docker run --name helloworld_container helloworld
# docker run - запустить контейнер
# --name helloworld_container - назвать контейнер "helloworld_container"
# helloworld - взять окружение для запуска из имейджа "helloworld"
Если вы всё сделали правильно, на этом этапе увидите
Hello world
в терминале 😉♻️ Приберём за собой и почистим созданные ранее ресурсы:
# Вывести список контейнеров. Флаг -a означает "в т.ч. завершившихся"
docker ps -a
# Удаляем наш контейнер
docker rm helloworld_container
# Вывести список имейджей
docker images
# Удаляем наш образ
docker rmi helloworld
# Удаляем базовый образ
docker rmi python:3
👍 На сегодня знакомство с docker закончим. Напоминаю, что контейнеры - это очень полезная технология, на её изучение имеет смысл потратить время 😉 Ставь огонёк, если хочешь более продвинутые туториалы по docker 🔥
#hardskills #containers #tools #Linux #docker
Отладка docker-контейнеров
В прошлый раз мы познакомились с docker-контейнерами, сегодня научимся их отлаживать.
🐳 Создадим
🔨 Собираем имейдж:
👟 Запускаем контейнер
🪲 Oh-oh…
Таким образом мы плавно приходим к теме сегодняшнего туториала: что делать, если контейнер не стартует?
1️⃣ Смотрим на статус контейнера:
Официальная документация говорит, что
2️⃣ Смотрим логи:
А в логах пусто 😢 В целом, это было ожидаемо, раз контейнер не стартанул. Если бы он “стартанул и сразу упал”, то в логах можно было бы что-то найти. Однако этим шагом никогда не стоит пренебрегать! Логи - это самый простой способ узнать, что происходит с вашим софтом. В случае любых проблем всегда смотрите логи. Ну а мы переходим к тяжёлой артиллерии:
3️⃣ Переопределим команду и запустим контейнер. Подключимся к нему и выполним исходную команду сами. Таким образом, мы получим боевое окружение, в котором сможем запустить свою команду с дополнительным дебагом без пересборки имейджа.
Обычно, на этом шаге исходная команда запускается с дополнительным дебагом. Например, можно с помощью аргументов командной строки включить более подробные логи, воспользоваться дебаггером или strace, ltrace.
❓ Но у нас всё заработало как есть, почему так? Это потому что в контейнере мы запускали код в оболочке интерпретатора sh, а докер при старте контейнера делает простой exec. Т.е. мы пытались запустить интерпретируемый код без интерпретатора, такая вот учебная бага 😊
❤️🩹 Давайте починим:
🤔 Итого, алгоритм дебага docker-контейнеров: уточняем статус
#hardskills #containers #tools #Linux #docker #debug #logging
В прошлый раз мы познакомились с docker-контейнерами, сегодня научимся их отлаживать.
🐳 Создадим
Dockerfile
:# Alpine - это минималистичный дистрибутив Linux
# Его любят использовать для контейнеров, потому что он очень мало весит
FROM "alpine"
# Бесконечно выводим "Hello world" с интервалом 0.5 секунды
CMD ["while", "[[ 1 ]];", "do", "echo", "'Hello world';", "sleep 0.5;", "done"]
🔨 Собираем имейдж:
docker build --tag dockerdebug .
👟 Запускаем контейнер
docker run --name dockerdebug_container -d dockerdebug
# Флаг -d нам раньше не встречался
# Это значит "запустить в бэкграунде", т.е. в режиме демона
🪲 Oh-oh…
41f8f5d28c04f9ba705b4482e26333152071fdd7c06af736933ebc9f0eb83cba
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "while": executable file not found in $PATH: unknown
Таким образом мы плавно приходим к теме сегодняшнего туториала: что делать, если контейнер не стартует?
1️⃣ Смотрим на статус контейнера:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41f8f5d28c04 dockerdebug "while '[[ 1 ]];' do…" 9 minutes ago Created dockerdebug_container
Официальная документация говорит, что
Created
значит “контейнер никогда не стартовал”. Окей, статус узнали, это хорошо. Уточнить текущее состояние важно для отладки, но никакой информации к размышлению это нам не дало. Идём дальше.2️⃣ Смотрим логи:
docker logs dockerdebug_container
А в логах пусто 😢 В целом, это было ожидаемо, раз контейнер не стартанул. Если бы он “стартанул и сразу упал”, то в логах можно было бы что-то найти. Однако этим шагом никогда не стоит пренебрегать! Логи - это самый простой способ узнать, что происходит с вашим софтом. В случае любых проблем всегда смотрите логи. Ну а мы переходим к тяжёлой артиллерии:
3️⃣ Переопределим команду и запустим контейнер. Подключимся к нему и выполним исходную команду сами. Таким образом, мы получим боевое окружение, в котором сможем запустить свою команду с дополнительным дебагом без пересборки имейджа.
# Удаляем багованный инстанс контейнера
docker rm dockerdebug_container
# Запускаем новый инстанс
# Обратите внимание на `sleep infinity` после тега имейджа
# Эта команда будет запущена при старте _вместо_ команды из Dockerfile
docker run --name dockerdebug_container -d dockerdebug sleep infinity
# Мы получили запущенное боевое окружение, в нём ничего не происходит
# Подключимся к этому окружению, чтобы увидеть мир "изнутри" контейнера
docker exec -it dockerdebug_container /bin/sh
# Мы получили шелл внутри контейнера. Выполним исходную команду.
while [[ 1 ]]; do echo 'Hello world'; sleep 0.5; done
Hello world
^C
# ^ работает, лол 0_o
Обычно, на этом шаге исходная команда запускается с дополнительным дебагом. Например, можно с помощью аргументов командной строки включить более подробные логи, воспользоваться дебаггером или strace, ltrace.
❓ Но у нас всё заработало как есть, почему так? Это потому что в контейнере мы запускали код в оболочке интерпретатора sh, а докер при старте контейнера делает простой exec. Т.е. мы пытались запустить интерпретируемый код без интерпретатора, такая вот учебная бага 😊
❤️🩹 Давайте починим:
FROM "alpine"
CMD ["/bin/sh", "-c", "while [[ 1 ]]; do echo 'Hello world'; sleep 0.5; done"]
docker stop dockerdebug_container
docker rm dockerdebug_container
docker rmi dockerdebug
docker build --tag dockerdebug .
docker run --name dockerdebug_container -d dockerdebug
docker logs dockerdebug_container
Hello world
🤔 Итого, алгоритм дебага docker-контейнеров: уточняем статус
docker ps
⇒ смотрим логи docker logs
⇒ если не помогло, переопределяем команду на sleep infinity
, подключаемся к контейнеру через docker exec
и применяем стандартные #debug инструменты 😉 Этот же алгоритм применим к кейсам, когда контейнер запустился, но не работает. Ставь огонёк, если было полезно 🔥#hardskills #containers #tools #Linux #docker #debug #logging
Telegram
Уймин - про разработку
Docker - контейнеризация, доступная всем
Контейнеры - самый современный и один из наиболее популярных способов поставки backend-приложений. Как работают контейнеры я уже писал, пора научиться ими пользоваться.
🐳 Docker - одна из причин популярности контейнеров…
Контейнеры - самый современный и один из наиболее популярных способов поставки backend-приложений. Как работают контейнеры я уже писал, пора научиться ими пользоваться.
🐳 Docker - одна из причин популярности контейнеров…
Синхронные vs асинхронные операции
Любую операцию (вызов функции, запись в файл, сетевой поход, запрос к БД и т.д.) можно выполнить двумя способами: синхронно или асинхронно. Рассмотрим разницу между двумя подходами.
➡️ Синхронно - значит “с ожиданием завершения операции”. Обычно, это поведение по-умолчанию: программист пишет инструкции в программе, они выполняются процессором в том порядке, в котором написаны. Процессор не начинает выполнение следующей инструкции, пока не закончит предыдущую.
🔀 Асинхронно - значит “без ожидания завершения операции”. В этом случае работает подход “выстрелил и забыл” - ждать, когда прилетят наши “пули” часто бывает необязательно. В современных языках программирования есть множество механизмов, с помощью которых можно обеспечить асинхронное выполнение: дочерние процессы, треды, корутины, файберы, фьючеры и т.п. Результат операции при этом получить можно, но не сразу, а “когда-нибудь потом”.
🤔 Зачем вообще нужно асинхронное выполнение? Давайте сравним два подхода и определим, чем отличаются их свойства.
1️⃣ Асинхронное выполнение тесно связано с таким свойством ПО, как многозадачность. Это значит, что несколько задач должны выполняться параллельно. С точки зрения программиста, для этого нужно запустить несколько операций без ожидания их заверщения, то есть асинхронно. Так что, когда ОС одновременно запускает несколько программ или web-браузер скачивает параллельно несколько ресурсов, под капотом там работает асинхронный код.
2️⃣ Асинхронный код может эффективнее обрабатывать блокировки. Для быстрых операций вроде сложения или доступа к оперативной памяти асинхронное выполнение часто не имеет смысла. Но есть операции медленные: сетевые походы, работа с жёстким диском, работа с мьютексами. В этих операциях часто нужно подождать, пока система не будет готова к выполнению операции. При этом происходит блокировка выполнения - система простаивает во время ожидания. В случае синхронного выполнения простой неизбежен, а в случае асинхронного - из множества параллельных задач можно выполнить любую, готовую к выполнению, и избежать простоя.
3️⃣ Как следствие, асинхронного ПО использует аппаратное обеспечение более эффективно, т.к. снижается время простоя в ожидании блокировок.
4️⃣ При всех преимуществах асинхронного ПО, у него есть один существенный недостаток: код становится сложнее. Программисту надо решать:
- Какие задачи надо выполнять асинхронно?
- Какие механизмы ЯП и ОС для этого выбрать?
- Как обеспечить синхронизацию задач (переход от асинхронного обратно к синхронному выполнению)?
Всё это усложняет чтение асинхронного кода, делает его поддержку более дорогой и может приводить к появлению целых классов новых ошибок.
⚖️ Вот такой вот интересный трейдоф: производительность vs простота. Что надо запомнить:
- Синхронное и асинхронное выполнение - это фундаментальная концепция. С ней можно встретиться в разных областях IT и за пределами отрасли.
- Асинхронное выполнение имеет смысл для тяжёлых операций. Для них асинхронность может повысить эффективность использования ресурсов.
- Повышение производительности не бесплатное: за него мы платим сложностью системы.
Ставь огонёк, если интересна тема асинхронщины и параллельного выполнения кода 🔥
#theory #concurrency #pattern
Любую операцию (вызов функции, запись в файл, сетевой поход, запрос к БД и т.д.) можно выполнить двумя способами: синхронно или асинхронно. Рассмотрим разницу между двумя подходами.
➡️ Синхронно - значит “с ожиданием завершения операции”. Обычно, это поведение по-умолчанию: программист пишет инструкции в программе, они выполняются процессором в том порядке, в котором написаны. Процессор не начинает выполнение следующей инструкции, пока не закончит предыдущую.
🔀 Асинхронно - значит “без ожидания завершения операции”. В этом случае работает подход “выстрелил и забыл” - ждать, когда прилетят наши “пули” часто бывает необязательно. В современных языках программирования есть множество механизмов, с помощью которых можно обеспечить асинхронное выполнение: дочерние процессы, треды, корутины, файберы, фьючеры и т.п. Результат операции при этом получить можно, но не сразу, а “когда-нибудь потом”.
🤔 Зачем вообще нужно асинхронное выполнение? Давайте сравним два подхода и определим, чем отличаются их свойства.
1️⃣ Асинхронное выполнение тесно связано с таким свойством ПО, как многозадачность. Это значит, что несколько задач должны выполняться параллельно. С точки зрения программиста, для этого нужно запустить несколько операций без ожидания их заверщения, то есть асинхронно. Так что, когда ОС одновременно запускает несколько программ или web-браузер скачивает параллельно несколько ресурсов, под капотом там работает асинхронный код.
2️⃣ Асинхронный код может эффективнее обрабатывать блокировки. Для быстрых операций вроде сложения или доступа к оперативной памяти асинхронное выполнение часто не имеет смысла. Но есть операции медленные: сетевые походы, работа с жёстким диском, работа с мьютексами. В этих операциях часто нужно подождать, пока система не будет готова к выполнению операции. При этом происходит блокировка выполнения - система простаивает во время ожидания. В случае синхронного выполнения простой неизбежен, а в случае асинхронного - из множества параллельных задач можно выполнить любую, готовую к выполнению, и избежать простоя.
3️⃣ Как следствие, асинхронного ПО использует аппаратное обеспечение более эффективно, т.к. снижается время простоя в ожидании блокировок.
4️⃣ При всех преимуществах асинхронного ПО, у него есть один существенный недостаток: код становится сложнее. Программисту надо решать:
- Какие задачи надо выполнять асинхронно?
- Какие механизмы ЯП и ОС для этого выбрать?
- Как обеспечить синхронизацию задач (переход от асинхронного обратно к синхронному выполнению)?
Всё это усложняет чтение асинхронного кода, делает его поддержку более дорогой и может приводить к появлению целых классов новых ошибок.
⚖️ Вот такой вот интересный трейдоф: производительность vs простота. Что надо запомнить:
- Синхронное и асинхронное выполнение - это фундаментальная концепция. С ней можно встретиться в разных областях IT и за пределами отрасли.
- Асинхронное выполнение имеет смысл для тяжёлых операций. Для них асинхронность может повысить эффективность использования ресурсов.
- Повышение производительности не бесплатное: за него мы платим сложностью системы.
Ставь огонёк, если интересна тема асинхронщины и параллельного выполнения кода 🔥
#theory #concurrency #pattern
Сортировка подсчётом
В универе у меня была лаба: надо было отсортировать массив чисел, посчитать алгоритмическую сложность и количество сравнений.
📶 Обычно сортировки как выглядят? Проходишь по массиву, сравниваешь элементы друг с другом, меняешь местами по результатам сравнения. Если тебе нужно сравнивать каждый с каждым, то сложность алгоритма
🎬 В общем-то, ничего нового, визуализации алгоритмов сортировки с помощью народных танцев на ютубе все смотрели. Вот пример за N квадрат, а вот за логарифм.
🤔 А что, если я скажу, что сортировку можно написать вообще без сравнений? На лабе я выдал примерно такой код:
^ позапускать можно здесь
🤯 У препода от такого глаза на лоб полезли. “И сколько тут сравнений?” - спросил он. “Ноль” - ответил я 😎 И кстати, оно работает за
🔥 В общем, думайте нестандартно, иногда даже самые глупые алгоритмы могут прекрасно работать. И обращайте внимание на ограничения, применимые ко входным данным. Это фундаментальная закономерность: чем больше ограничений можно применить ко входным данным, тем проще и эффективнее может быть алгоритм их обработки.
#theory #coding #algorithm
В универе у меня была лаба: надо было отсортировать массив чисел, посчитать алгоритмическую сложность и количество сравнений.
📶 Обычно сортировки как выглядят? Проходишь по массиву, сравниваешь элементы друг с другом, меняешь местами по результатам сравнения. Если тебе нужно сравнивать каждый с каждым, то сложность алгоритма
O(N²)
, это медленно 👎. Если реализовать более сложный алгоритм, каким-то образом бить массив на группы и работать уже с ними, то можно добиться сложности алгоритма O(N・log(N))
, это быстро 👍.🎬 В общем-то, ничего нового, визуализации алгоритмов сортировки с помощью народных танцев на ютубе все смотрели. Вот пример за N квадрат, а вот за логарифм.
🤔 А что, если я скажу, что сортировку можно написать вообще без сравнений? На лабе я выдал примерно такой код:
#include <stdint.h>
#include <string.h>
void count_sort(uint8_t *arr, size_t arr_len)
{
// У нас в аргументах массив байт. Каждый байт может принимать 256 разных значений
#define VALUES_CNT 256
// Аллоцируем массив счётчиков, для коротких типов данных можно в статической памяти
static uint64_t cnt[VALUES_CNT];
memset(cnt, 0, sizeof(uint64_t) * VALUES_CNT);
// Считаем вхождение каждого элемента в массив
for (size_t i = 0; i < arr_len; i++)
cnt[arr[i]]++;
// Переписываем массив
// Позиция в результирующем массиве
size_t i = 0;
// Проходим по массиву счётчиков
for (size_t b = 0; b < VALUES_CNT; b++) {
// Повторяем каждый элемент b в результирующем массиве c раз
for (uint64_t c = 0; c < cnt[b]; c++) {
arr[i] = (uint8_t)b;
i++;
}
}
}
^ позапускать можно здесь
🤯 У препода от такого глаза на лоб полезли. “И сколько тут сравнений?” - спросил он. “Ноль” - ответил я 😎 И кстати, оно работает за
O(N)
- это даже быстрее, чем за логарифм. Но ограничений, конечно, много: сортировать так можно только массивы с малой областью допустимых значений.🔥 В общем, думайте нестандартно, иногда даже самые глупые алгоритмы могут прекрасно работать. И обращайте внимание на ограничения, применимые ко входным данным. Это фундаментальная закономерность: чем больше ограничений можно применить ко входным данным, тем проще и эффективнее может быть алгоритм их обработки.
#theory #coding #algorithm
strace - дебажим системные вызовы
Иногда возникают задачи отдебажить “чужой код”. Например, какое-нибудь проприетарное приложение или стороннюю библиотеку. И всё хорошо, если в этом “чужом коде” всё в порядке с observability: есть понятные логи, по которым можно разобрать причины проблем. А что если нет? Что, если чужой софт просто падает с ошибкой без каких-либо сообщений? Или проблема находится на крайне низком уровне: стандартная библиотека/glibc/ядро ОС?
💣 В этом случае приходится подключать тяжёлую артиллерию. Для меня открытием стала утилита strace. Она позволяет отслеживать выполняемые программой системные вызовы и возвращаемые значения. Давайте сразу пример, вот так выглядит простой вызов
🤨 Чего тут можно увидеть?
1️⃣ Мы выполнили
2️⃣ Процесс инициализировался, подгрузил динамические библиотеки
3️⃣ Потом наш процесс сделал
🤓 Полезные флаги
-
-
-
-
🔥 strace позволяет получить дебаг информацию о процессе прямо от ОС - это невероятно полезно при низкоуровневых проблемах и при отладке "чёрных ящиков".
#hardskills #Linux #tools #debug
Иногда возникают задачи отдебажить “чужой код”. Например, какое-нибудь проприетарное приложение или стороннюю библиотеку. И всё хорошо, если в этом “чужом коде” всё в порядке с observability: есть понятные логи, по которым можно разобрать причины проблем. А что если нет? Что, если чужой софт просто падает с ошибкой без каких-либо сообщений? Или проблема находится на крайне низком уровне: стандартная библиотека/glibc/ядро ОС?
💣 В этом случае приходится подключать тяжёлую артиллерию. Для меня открытием стала утилита strace. Она позволяет отслеживать выполняемые программой системные вызовы и возвращаемые значения. Давайте сразу пример, вот так выглядит простой вызов
echo
под strace
:strace echo "Hello world"
execve("/usr/bin/echo", ["echo", "Hello world"], 0x7ffd82112798 /* 36 vars */) = 0
brk(NULL) = 0x56501e97b000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffe71bfbd20) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe1e2ba4000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=16407, ...}) = 0
mmap(NULL, 16407, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe1e2b9f000
close(3) = 0
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\256\3\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2089984, ...}) = 0
lseek(3, 808, SEEK_SET) = 808
read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32
mmap(NULL, 3950816, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe1e25b3000
mprotect(0x7fe1e276e000, 2097152, PROT_NONE) = 0
mmap(0x7fe1e296e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bb000) = 0x7fe1e296e000
mmap(0x7fe1e2974000, 14560, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe1e2974000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe1e2b9d000
arch_prctl(ARCH_SET_FS, 0x7fe1e2ba5680) = 0
mprotect(0x7fe1e296e000, 16384, PROT_READ) = 0
mprotect(0x56501e0a9000, 4096, PROT_READ) = 0
mprotect(0x7fe1e2ba6000, 4096, PROT_READ) = 0
munmap(0x7fe1e2b9f000, 16407) = 0
getrandom("\x75\x94\x42\x9a\x46\x55\x16\x6d", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x56501e97b000
brk(0x56501e99c000) = 0x56501e99c000
brk(NULL) = 0x56501e99c000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=217804320, ...}) = 0
mmap(NULL, 217804320, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe1d55fc000
close(3) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x5), ...}) = 0
write(1, "Hello world\n", 12Hello world
) = 12
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
🤨 Чего тут можно увидеть?
1️⃣ Мы выполнили
execve
на файл /usr/bin/echo
2️⃣ Процесс инициализировался, подгрузил динамические библиотеки
3️⃣ Потом наш процесс сделал
write
в stdout
и завершился🤓 Полезные флаги
strace
:-
-p <PID>
- подключиться можно к существующему процессу, не обязательно запускать его под strace.-
--follow-forks
- если процесс создаёт дочерние процессы, по-умолчанию strace будет следить за родительским. Этот флаг меняет поведение, strace начинает следить за дочерними процессами.-
-e
- к выхлопу strace можно применять фильтры-
-o <FILE>
- выхлоп можно направлять в файл (по-умолчанию он летит в stderr
)🔥 strace позволяет получить дебаг информацию о процессе прямо от ОС - это невероятно полезно при низкоуровневых проблемах и при отладке "чёрных ящиков".
#hardskills #Linux #tools #debug
Утилизация ресурсов сервера
Нет, речь не про переработку отходов 😊 У слова утилизация в контексте серверной разработки есть ещё одно значение: эффективность использования ресурсов.
👀 Утилизация считается по формуле:
🤔 Почему это важно? Во-первых, утилизация тесно связана с экономической эффективностью. Если в вашей системе тонна ресурсов, которыми никто не пользуется, то говорят, что система недоутилизирована. В контексте бэкенда это значит, что мы купили кучу дорогущих серверов и не пользуемся ими, можно было бы и не покупать 😊
😮 Так значит надо максимизировать утилизацию! Нет, не значит 🙅 Когда утилизация приближается к 100%, в системе может наступить дефицит ресурсов. Это может привести к снижению качества предоставляемого сервиса или полному отказу в обслуживании. На этом эффекте, например, основаны широко известные DoS-атаки.
❗️ За утилизацией аппаратных ресурсов в системе надо следить. При этом за каждым ресурсом нужно следить по-отдельности, т.к. дефицит любого ресурса может привести к сбоям. Какие ресурсы есть на сервере?
1️⃣ Оперативная память - следим за объёмом доступной памяти. Не забываем, что современные ОС используют свободную оперативку под файловые кеши. Память, занятую этими кешами, часто тоже можно считать “свободной”.
2️⃣ Диски - тут чуть сложнее, надо следить не только за размером свободного места, но и за iops’ами. Диски могут выполнить ограниченное количество операций ввода/вывода за единицу времени, поэтому важно не только количество хранимых данных, но и частота их обновления на диске.
3️⃣ Сеть - следим за пропускной способностью, сколько данных мы можем прокачать через сеть за единицу времени. Не забываем, что на сети пропускная способность измеряется в гигабитах/сек - не в гигабайтах/сек. В 8 раз ошибаться не надо 😊
4️⃣ CPU (и другие вычислители при наличии: GPU, APU, аппаратные ускорители) - тут всё сложнее. Потребление процессора обычно измеряется в процентах: какую долю времени процессор занят вычислениями (т.е. не простаивает). Расчёт утилизации усложняется коэффициентами. Современные процессоры многоядерные - каждое ядро может вести вычисления независимо от других ядер (утилизация у него тоже независимая). Современные ядра поддерживают гипертрединг - это когда 1 ядро может выполнять несколько (обычно 2) независимых потока вычислений. Лимит вычислительных ресурсов можно посчитать так:
💎 Мониторинг аппаратных ресурсов нужен всем, поэтому в опенсорсе для этого есть готовые инструменты. Свои тулзы изобретать ну нужно, лучше поставьте себе Node Exporter.
👨💻 Для программиста мониторинг утилизации хардварных ресурсов не менее важен, чем для сисадмина. Там можно увидеть 2 класса проблем с производительностью:
1️⃣ Система не может принимать новых клиентов, но хардвар недоутилизирован - программист написал немасштабируемый код.
2️⃣ Поделите утилизацию ресурса на кол-во полезных операций. Если числа слишком большие - программист написал неэффективный код.
🔥 В высоконагруженных системах обычно стараются поддерживать такую утилизацию, чтобы система могла пережить рост нагрузки x2. Это число не выбито в камне и может меняться в зависимости от требований бизнеса. Однако, вне зависимости от желаемого режима работы вашей системы, мониторинг и контроль утилизации аппаратных ресурсов обязателен для всех серверных систем.
#theory #concurrency #highload #bestpractice
Нет, речь не про переработку отходов 😊 У слова утилизация в контексте серверной разработки есть ещё одно значение: эффективность использования ресурсов.
👀 Утилизация считается по формуле:
<утилизация (%)> = <потребление ресурса> / <кол-во ресурса в системе> * 100
. Давайте на примере. Пусть в вашем сервере 32 GB
оперативной памяти. На нём запущено 1 приложение, которое потребляет 4 GB
. Тогда утилизация памяти вашего сервера 4 / 32 * 100 = 12.5%
.🤔 Почему это важно? Во-первых, утилизация тесно связана с экономической эффективностью. Если в вашей системе тонна ресурсов, которыми никто не пользуется, то говорят, что система недоутилизирована. В контексте бэкенда это значит, что мы купили кучу дорогущих серверов и не пользуемся ими, можно было бы и не покупать 😊
😮 Так значит надо максимизировать утилизацию! Нет, не значит 🙅 Когда утилизация приближается к 100%, в системе может наступить дефицит ресурсов. Это может привести к снижению качества предоставляемого сервиса или полному отказу в обслуживании. На этом эффекте, например, основаны широко известные DoS-атаки.
❗️ За утилизацией аппаратных ресурсов в системе надо следить. При этом за каждым ресурсом нужно следить по-отдельности, т.к. дефицит любого ресурса может привести к сбоям. Какие ресурсы есть на сервере?
1️⃣ Оперативная память - следим за объёмом доступной памяти. Не забываем, что современные ОС используют свободную оперативку под файловые кеши. Память, занятую этими кешами, часто тоже можно считать “свободной”.
2️⃣ Диски - тут чуть сложнее, надо следить не только за размером свободного места, но и за iops’ами. Диски могут выполнить ограниченное количество операций ввода/вывода за единицу времени, поэтому важно не только количество хранимых данных, но и частота их обновления на диске.
3️⃣ Сеть - следим за пропускной способностью, сколько данных мы можем прокачать через сеть за единицу времени. Не забываем, что на сети пропускная способность измеряется в гигабитах/сек - не в гигабайтах/сек. В 8 раз ошибаться не надо 😊
4️⃣ CPU (и другие вычислители при наличии: GPU, APU, аппаратные ускорители) - тут всё сложнее. Потребление процессора обычно измеряется в процентах: какую долю времени процессор занят вычислениями (т.е. не простаивает). Расчёт утилизации усложняется коэффициентами. Современные процессоры многоядерные - каждое ядро может вести вычисления независимо от других ядер (утилизация у него тоже независимая). Современные ядра поддерживают гипертрединг - это когда 1 ядро может выполнять несколько (обычно 2) независимых потока вычислений. Лимит вычислительных ресурсов можно посчитать так:
<кол-во процессоров> * <кол-во ядер> * <коэффициент гипертрединга> * 100%
Так что если вы увидите, что сервер жрёт 1000% СPU, не пугайтесь - это значит, что вы утилизировали 10 ядер.💎 Мониторинг аппаратных ресурсов нужен всем, поэтому в опенсорсе для этого есть готовые инструменты. Свои тулзы изобретать ну нужно, лучше поставьте себе Node Exporter.
👨💻 Для программиста мониторинг утилизации хардварных ресурсов не менее важен, чем для сисадмина. Там можно увидеть 2 класса проблем с производительностью:
1️⃣ Система не может принимать новых клиентов, но хардвар недоутилизирован - программист написал немасштабируемый код.
2️⃣ Поделите утилизацию ресурса на кол-во полезных операций. Если числа слишком большие - программист написал неэффективный код.
🔥 В высоконагруженных системах обычно стараются поддерживать такую утилизацию, чтобы система могла пережить рост нагрузки x2. Это число не выбито в камне и может меняться в зависимости от требований бизнеса. Однако, вне зависимости от желаемого режима работы вашей системы, мониторинг и контроль утилизации аппаратных ресурсов обязателен для всех серверных систем.
#theory #concurrency #highload #bestpractice
Потоки vs процессы: масштабирование по ядрам CPU
Это база - спрашиваю на каждом собеседовании 😉 С ростом нагрузки на систему становится критически важно грамотно утилизировать аппаратные ресурсы. Сегодня обсудим CPU, про него имеет смысл говорить отдельно, т.к. грамотная утилизация процессора требует определённой квалификации от программиста.
🤔 Сколько процессора утилизирует этот код?
В абсолютных числах - 1 поток. В процентах - зависит от машины, на которой код запущен. Надо поделить 1 на количество ядер процессора, помноженное на коэффициент гипертрединга. Подробнее писал в посте про утилизацию.
😮 Даже самый неоптимальный код по-умолчанию не может утилизировать все вычислительные ресурсы сервера, об этом должен позаботиться программист.
📖 Доступом к процессору, как и ко всем другим аппаратным ресурсам, управляет операционная система. Современные процессоры - многоядерные, ядра процессора могут выполнять вычисления параллельно, независимо друг от друга. Чтобы приложения могли использовать эту возможность, ОС предоставляет асинхронный API для параллельных вычислений, в основе которого лежит 2 концепции: потоки и процессы.
▶️ Процесс (process) - это запущенное приложение. У каждого процесса есть эксклюзивный доступ к ресурсам: аллоцированной памяти, файловым дескрипторам и т.д. Процессы максимально изолированы: они не имеют доступа к ресурсам друг друга и обладают независимым жизненным циклом. Любое взаимодействие между процессами требует написания кода с использованием специальных механизмов IPC (inter-process communication) - файлов, пайпов, сокетов, сигналов, и других.
➡️ Поток (thread) - это последовательность вычислений. У каждого процесса под капотом по-умолчанию есть 1 поток, все вычисления выполняются в нём последовательно. Ресурсы внутри процесса общие для всех потоков. Если кто-то открыл файл или аллоцировал память, все потоки могут этот ресурс прочитать и записать без необходимости использовать какие-то дополнительные механизмы.
🔀 По-настоящему параллельные вычисления, с утилизацией нескольких ядер CPU, можно организовать двумя способами:
- запустив дополнительные потоки
- создав дочерние процессы
☝️ Ключевая разница между двумя способами в разграничении доступа к ресурсам: у процессов эксклюзивные ресурсы, у потоков общие.
📈 Максимальной производительности можно достичь, когда кол-во запущенных потоков в системе равняется кол-ву потоков в архитектуре CPU. В этом случае каждый поток может выполняться непрерывно.
🤔 Зачем знать низкоуровневые API в 2024? У нас же есть языки программирования высокого уровня со всевозможными
⚠️ Дело в том, что ОС кроме процессов и тредов других асинхронных моделей не знает. Поэтому, если вам нужно больше 1 ядра CPU, понимание и использование низкоуровневых API необходимо. Например:
- В
- В
- В
- А в
🔥 Не забывайте про процессы и потоки - это фундамент, на котором стоят все параллельные вычисления. А параллельные вычисления - ключ к высокой производительности 😉
#theory #Linux #concurrency #tools #coding #pattern #highload
Это база - спрашиваю на каждом собеседовании 😉 С ростом нагрузки на систему становится критически важно грамотно утилизировать аппаратные ресурсы. Сегодня обсудим CPU, про него имеет смысл говорить отдельно, т.к. грамотная утилизация процессора требует определённой квалификации от программиста.
🤔 Сколько процессора утилизирует этот код?
while (true) {}
😮 Даже самый неоптимальный код по-умолчанию не может утилизировать все вычислительные ресурсы сервера, об этом должен позаботиться программист.
📖 Доступом к процессору, как и ко всем другим аппаратным ресурсам, управляет операционная система. Современные процессоры - многоядерные, ядра процессора могут выполнять вычисления параллельно, независимо друг от друга. Чтобы приложения могли использовать эту возможность, ОС предоставляет асинхронный API для параллельных вычислений, в основе которого лежит 2 концепции: потоки и процессы.
▶️ Процесс (process) - это запущенное приложение. У каждого процесса есть эксклюзивный доступ к ресурсам: аллоцированной памяти, файловым дескрипторам и т.д. Процессы максимально изолированы: они не имеют доступа к ресурсам друг друга и обладают независимым жизненным циклом. Любое взаимодействие между процессами требует написания кода с использованием специальных механизмов IPC (inter-process communication) - файлов, пайпов, сокетов, сигналов, и других.
➡️ Поток (thread) - это последовательность вычислений. У каждого процесса под капотом по-умолчанию есть 1 поток, все вычисления выполняются в нём последовательно. Ресурсы внутри процесса общие для всех потоков. Если кто-то открыл файл или аллоцировал память, все потоки могут этот ресурс прочитать и записать без необходимости использовать какие-то дополнительные механизмы.
🔀 По-настоящему параллельные вычисления, с утилизацией нескольких ядер CPU, можно организовать двумя способами:
- запустив дополнительные потоки
- создав дочерние процессы
☝️ Ключевая разница между двумя способами в разграничении доступа к ресурсам: у процессов эксклюзивные ресурсы, у потоков общие.
📈 Максимальной производительности можно достичь, когда кол-во запущенных потоков в системе равняется кол-ву потоков в архитектуре CPU. В этом случае каждый поток может выполняться непрерывно.
🤔 Зачем знать низкоуровневые API в 2024? У нас же есть языки программирования высокого уровня со всевозможными
async/await
, go func () { }
и другими высокоуровневыми асинхронными API.⚠️ Дело в том, что ОС кроме процессов и тредов других асинхронных моделей не знает. Поэтому, если вам нужно больше 1 ядра CPU, понимание и использование низкоуровневых API необходимо. Например:
- В
go
есть GOMAXPROCS- В
Node.js
есть --v8-pool-size- В
Python
есть threading и multiprocessing- А в
C/C++
можно использовать fork(2) и pthread_create(3) напрямую🔥 Не забывайте про процессы и потоки - это фундамент, на котором стоят все параллельные вычисления. А параллельные вычисления - ключ к высокой производительности 😉
#theory #Linux #concurrency #tools #coding #pattern #highload
5 навыков программиста, которым не учат в универе. Как научиться самому?
1️⃣ Командная разработка: все коммерческие проекты делаются командами, в универе же большая часть работ - индивидуальная. Это не позволяет освоить такие темы как: git, код ревью, декомпозиция проекта на задачи. Как научиться самому:
- Делайте лабы/курсачи/пэт-проекты с друзьями
- Обменивайтесь кодом через github
2️⃣ Чтение кода: уже писал, насколько это важно. Академическая программа, к сожалению, обычно не предполагает тренировки этого навыка. Как научиться самому:
- Вписывайтесь в командные проекты, читайте код коллег
- Используйте в своих проектах сторонние библиотеки с открытым кодом, читайте их исходники
3️⃣ Работа в терминале: как пользователи мы привыкли к графическим интерфейсам со всевозможными окошками и кнопочками. Программисты много работают с текстовыми интерфейсами, где кроме букв ничего нет. Для большинства разработчиков это must have, но в универах эта тема непопулярна. Как научиться самому:
- Поставьте себе Linux, можно второй операционкой. Если боитесь, можете арендовать виртуальный сервер в облаке
- Все задачи на Linux решайте исключительно в терминале (командной строке)
- Начать изучение можно с команд:
4️⃣ Обеспечение observability: реальные приложения пишут логи и метрики, это позволяет понять в процессе работы, всё ли с приложением хорошо и не возникают ли в нём ошибки. В универах эту тему обычно опускают, потому что цикл жизни учебного софта очень короткий: сдал лабу и забыл. Как научиться самому:
- В следующей своей программе начните писать в консоль ключевые события, чтобы можно было понять, что программа делает под капотом не заглядывая в исходный код
- Попробуйте записать метрики в Prometheus или Graphite, их можно поднять в docker прямо на своём компе. Посчитайте, например, количество запросов пользователя и время на их выполнение
5️⃣ Отладка: процессу поиска и устранения багов в ПО толком никто не учит. Как научиться самому:
- А хз, отдельных ресурсов, посвящённых дебагу, я не знаю. Пишите больше кода и посмотрите что есть на этом канале по тегу #debug - там есть полезные механики
🤔 Универ - крайне интересное место. С одной стороны, он даёт базу по многим направлениям, которую изучающим программирование самостоятельно наработать очень трудно. С другой стороны, выше я перечислил целых 5 базовых навыков, необходимых для работы профессиональному программисту, которым в универе не обучают.
А что вы думаете про высшее образование? Какие темы стоит раскрыть подробнее? Напишите об этом в комменты 👇 а то чё они пустые стоят 🤪
#career #selflearning
1️⃣ Командная разработка: все коммерческие проекты делаются командами, в универе же большая часть работ - индивидуальная. Это не позволяет освоить такие темы как: git, код ревью, декомпозиция проекта на задачи. Как научиться самому:
- Делайте лабы/курсачи/пэт-проекты с друзьями
- Обменивайтесь кодом через github
2️⃣ Чтение кода: уже писал, насколько это важно. Академическая программа, к сожалению, обычно не предполагает тренировки этого навыка. Как научиться самому:
- Вписывайтесь в командные проекты, читайте код коллег
- Используйте в своих проектах сторонние библиотеки с открытым кодом, читайте их исходники
3️⃣ Работа в терминале: как пользователи мы привыкли к графическим интерфейсам со всевозможными окошками и кнопочками. Программисты много работают с текстовыми интерфейсами, где кроме букв ничего нет. Для большинства разработчиков это must have, но в универах эта тема непопулярна. Как научиться самому:
- Поставьте себе Linux, можно второй операционкой. Если боитесь, можете арендовать виртуальный сервер в облаке
- Все задачи на Linux решайте исключительно в терминале (командной строке)
- Начать изучение можно с команд:
whoami
, echo
, ls
, cd
, mkdir
, touch
, cp
, mv
, ps
, top
, пакетный менеджер (apt
или yum
), chown
, chgrp
, chmod
, cat
, head
, tail
, less
, git
← это минимальный набор, которого я жду от джуна. Будет плюсом: grep
, sed
, curl
4️⃣ Обеспечение observability: реальные приложения пишут логи и метрики, это позволяет понять в процессе работы, всё ли с приложением хорошо и не возникают ли в нём ошибки. В универах эту тему обычно опускают, потому что цикл жизни учебного софта очень короткий: сдал лабу и забыл. Как научиться самому:
- В следующей своей программе начните писать в консоль ключевые события, чтобы можно было понять, что программа делает под капотом не заглядывая в исходный код
- Попробуйте записать метрики в Prometheus или Graphite, их можно поднять в docker прямо на своём компе. Посчитайте, например, количество запросов пользователя и время на их выполнение
5️⃣ Отладка: процессу поиска и устранения багов в ПО толком никто не учит. Как научиться самому:
- А хз, отдельных ресурсов, посвящённых дебагу, я не знаю. Пишите больше кода и посмотрите что есть на этом канале по тегу #debug - там есть полезные механики
🤔 Универ - крайне интересное место. С одной стороны, он даёт базу по многим направлениям, которую изучающим программирование самостоятельно наработать очень трудно. С другой стороны, выше я перечислил целых 5 базовых навыков, необходимых для работы профессиональному программисту, которым в универе не обучают.
А что вы думаете про высшее образование? Какие темы стоит раскрыть подробнее? Напишите об этом в комменты 👇 а то чё они пустые стоят 🤪
#career #selflearning
Intern → Lead
Я заработал повышение 5-ый год подряд 🎉 Теперь я Ведущий программист. Думал ли я, что зайду так далеко, прорешивая задачник Pascal ABC, или собирая первое приложение под старенький Android?)
Сегодня хочу поговорить про профессиональный путь. Как мне удалось построить карьеру и какие были ключевые драйверы моего роста. Что можно сделать, чтобы мой путь повторить?
→ Intern Вот ты пришёл на первое место работы. Получил пропуск, в VK нам ещё макбуки выдают. Что дальше?
- Ты очень быстро обнаружишь, что бесполезен. Это нормально, это просто надо пережить. На моей памяти, синдром самозванца преследует всех на старте карьеры.
- На этом этапе надо проявить свои софт-скиллы. Работай чуть больше, чем требуется. Прилагай максимум усилий, чтобы решить задачу. Общайся с коллегами, задавай вопросы. Интересуйся деталями своего проекта.
Intern → Junior Если всё сделаешь правильно и сможешь влиться в коллектив, тебя возьмут на полный рабочий день. И тут важно понимать, что джун всё ещё бесполезен для компании. Ты можешь решать какие-то задачи, но скорость и качество будут низкими.
- Главный драйвер роста на этом этапе - опыт. Продолжай работать и решать задачи. Со временем, ты разберёшься, как делать это быстрее и лучше.
- Обязательно проходить код-ревью у коллег. Крайне желательно и самому ревьюить код - нарабатывать насмотренность и навык чтения кода.
- Где-то через полгода работы стоит взять ответственность за какой-то участок проекта. Пусть он будет небольшим и неважным, но это должен быть такой кусок, в котором ты сможешь решить несколько последовательных задач, самостоятельно выбирая подход к решению и дописывая свой собственный код.
Junior → Middle В среднем через год-полтора джун может наработать достаточный навык, чтобы перейти в роль мидла. Самое важное изменение здесь организационное: мидл может решать задачи достаточно быстро и эффективно, чтобы быть полезным для компании. Этот качественный переход приводит к удивительным изменениям в зарплате: рост до x2 - норма. В моём случае это было +70% - были времена :)
- Часто именно в этот момент программисты меняют компанию. Это логично, потому что работник может заметить рост своих навыков раньше, чем его начальник.
- Однако, я считаю ошибкой преждевременную смену работы. Для начала, оглянись по сторонам. Тебя устраивает команда? Твои проекты? Если ответ “да”, то начни разговор о повышении со своим руководителем. Подготовь список своих достижений, пусть они покажут, как выросли твои навыки. Если ты сможешь получить повышение на текущем месте - на новой должности у тебя останутся знания о той системе, которую ты разрабатываешь. Это позволит расти быстрее.
- С другой стороны, если договориться о повышении не получается, можешь смело выходить на рынок труда. Переход Junior → Middle со сменой компании - распространённое явление, нет смысла сидеть джуном, если ты готов выполнять роль мидла. В процессе поиска работы ты либо сможешь получить хороший рейз, либо поймёшь, по каким темам твои навыки проседают. В любом случае будешь в выигрыше 😉
Пост немного затянулся, предлагаю сделать паузу. Накидайте огонёчков и я сделаю вторую часть, в которой расскажу, как рос с мидла до сеньора и дальше. Продолжай делать системы, чтобы пацанам было не стыдно показать, и карьера придёт. Я верю, у тебя получится 🔥
#career #selflearning
Я заработал повышение 5-ый год подряд 🎉 Теперь я Ведущий программист. Думал ли я, что зайду так далеко, прорешивая задачник Pascal ABC, или собирая первое приложение под старенький Android?)
Сегодня хочу поговорить про профессиональный путь. Как мне удалось построить карьеру и какие были ключевые драйверы моего роста. Что можно сделать, чтобы мой путь повторить?
→ Intern Вот ты пришёл на первое место работы. Получил пропуск, в VK нам ещё макбуки выдают. Что дальше?
- Ты очень быстро обнаружишь, что бесполезен. Это нормально, это просто надо пережить. На моей памяти, синдром самозванца преследует всех на старте карьеры.
- На этом этапе надо проявить свои софт-скиллы. Работай чуть больше, чем требуется. Прилагай максимум усилий, чтобы решить задачу. Общайся с коллегами, задавай вопросы. Интересуйся деталями своего проекта.
Intern → Junior Если всё сделаешь правильно и сможешь влиться в коллектив, тебя возьмут на полный рабочий день. И тут важно понимать, что джун всё ещё бесполезен для компании. Ты можешь решать какие-то задачи, но скорость и качество будут низкими.
- Главный драйвер роста на этом этапе - опыт. Продолжай работать и решать задачи. Со временем, ты разберёшься, как делать это быстрее и лучше.
- Обязательно проходить код-ревью у коллег. Крайне желательно и самому ревьюить код - нарабатывать насмотренность и навык чтения кода.
- Где-то через полгода работы стоит взять ответственность за какой-то участок проекта. Пусть он будет небольшим и неважным, но это должен быть такой кусок, в котором ты сможешь решить несколько последовательных задач, самостоятельно выбирая подход к решению и дописывая свой собственный код.
Junior → Middle В среднем через год-полтора джун может наработать достаточный навык, чтобы перейти в роль мидла. Самое важное изменение здесь организационное: мидл может решать задачи достаточно быстро и эффективно, чтобы быть полезным для компании. Этот качественный переход приводит к удивительным изменениям в зарплате: рост до x2 - норма. В моём случае это было +70% - были времена :)
- Часто именно в этот момент программисты меняют компанию. Это логично, потому что работник может заметить рост своих навыков раньше, чем его начальник.
- Однако, я считаю ошибкой преждевременную смену работы. Для начала, оглянись по сторонам. Тебя устраивает команда? Твои проекты? Если ответ “да”, то начни разговор о повышении со своим руководителем. Подготовь список своих достижений, пусть они покажут, как выросли твои навыки. Если ты сможешь получить повышение на текущем месте - на новой должности у тебя останутся знания о той системе, которую ты разрабатываешь. Это позволит расти быстрее.
- С другой стороны, если договориться о повышении не получается, можешь смело выходить на рынок труда. Переход Junior → Middle со сменой компании - распространённое явление, нет смысла сидеть джуном, если ты готов выполнять роль мидла. В процессе поиска работы ты либо сможешь получить хороший рейз, либо поймёшь, по каким темам твои навыки проседают. В любом случае будешь в выигрыше 😉
Пост немного затянулся, предлагаю сделать паузу. Накидайте огонёчков и я сделаю вторую часть, в которой расскажу, как рос с мидла до сеньора и дальше. Продолжай делать системы, чтобы пацанам было не стыдно показать, и карьера придёт. Я верю, у тебя получится 🔥
#career #selflearning
Intern → Lead (часть 2)
Как расти с позиции мидла? В первой части рассмотрели старт карьеры программиста, от стажёра до мидла. Сегодня обсудим, как расти от мидла до сеньора и выше.
Middle → Senior Прежде всего, не всем это нужно. Любой карьерный рост требует прилагать сверх-усилие. Если тебе комфортно на текущей позиции, никто тебя не осудит 😉. Но если хочется расти выше, вот что помогло мне:
- Найди наставника. Формально, ментора у меня не было. Но я выбрал самого шерстяного сеньора с самыми сильными лапищами из тех, с кем мне приходилось постоянно работать, и начал анализировать что он делает. Я начал копировать его подходы в архитектуре и написании кода, и это сработало: у меня начали получаться более безопасные, простые и качественные решения.
- Возьми проект, чтобы ты мог за него отвечать. Свой текущий проект на работе я делаю последние 3 года. Я считаю это одним из главных драйверов моей карьеры: с ним я вырос от мидла до ведущего, защитил диплом в универе, заонбордил в команду 3-ёх джунов и, спойлер, пойду выступать на SaintHighload++ в этом году. Продолжительный по времени проект развивает в тебе кучу компетенций “на вырост”: учит правильно выбирать архитектуру и нести ответственность за плохие решения, развивает навыки дебага, требует настройки observability и процесса QA. Все эти навыки со временем сделают из тебя сеньора.
- Бонус №1: преподавание. Преподавание помогает структурировать свои собственные знания, чтобы их было удобно передать другим. Попробуйте заонбордить в свой проект стажёра. Выступите в роли ментора для начинающего программиста. Подготовьте доклад/документацию по своему проекту для коллег. Выступите на митапе. Всё это отлично упорядочивает ваши знания и, в конечном итоге, позволяет учиться быстрее.
- Бонус №2: экспертиза в предметной области. Всю свою карьеру я построил в одной компании. Я работаю над Почтой Mail.ru, и она моя ровесница: Почту запустили в год моего рождения 😂. Когда работаешь над проектом с такой историей, на то, чтобы разобраться в нём, уходят годы. Это те знания, которые невозможно получить снаружи компании. И они становятся твоим конкурентным преимуществом: позволяют выполнять задачи быстрее и качественнее. Найди хорошую компанию и поработай в ней хотя бы 2-3 года - количество возможностей для роста сильно увеличится 😉
Senior → Lead. После сеньора остаётся не так много возможностей для роста. По сути есть два самых популярных трека: менеджерский (в тимлида) и экспертный (в ведущего разработчика). Экспертный трек - не самое частое явление, такая возможность есть не во всех компаниях.
- Если хочешь строить карьеру по экспертному треку, начинай делиться экспертизой. Тут преподавание превращается из бонуса в must have. Без этого будет крайне сложно убедить коллег и руководство в своей экспертности.
- Выбери специализацию. Сеньор может решать крайне широкий спектр задач и переход в экспертную роль этого не отнимает. Однако, чтобы выполнить такой переход эффективно, нужно сконцентрировать свои усилия на какой-то одной области. Такой подход позволит решать какой-то тип задач эффективнее всех остальных - это и называется экспертизой.
- Продолжай учиться. Ну уровне сеньора самое время начать потреблять хардкорный профессиональный контент. Это тот этап, когда профессиональные конференции и умные книжки становятся максимально полезными. Для мидла всё ещё важнее навыки практического решения задач, а вот сеньору может помочь вырасти понимание фундаментальных закономерностей и стандартных методологий.
Вот такой у меня карьерный трек. Я постарался изложить самые сильные драйверы роста, которые помогли мне двигаться по карьерной лестнице. Надеюсь, теперь они помогут тебе 🔥 А любые вопросы можешь задать в комменты 👇
#career #selflearning
Как расти с позиции мидла? В первой части рассмотрели старт карьеры программиста, от стажёра до мидла. Сегодня обсудим, как расти от мидла до сеньора и выше.
Middle → Senior Прежде всего, не всем это нужно. Любой карьерный рост требует прилагать сверх-усилие. Если тебе комфортно на текущей позиции, никто тебя не осудит 😉. Но если хочется расти выше, вот что помогло мне:
- Найди наставника. Формально, ментора у меня не было. Но я выбрал самого шерстяного сеньора с самыми сильными лапищами из тех, с кем мне приходилось постоянно работать, и начал анализировать что он делает. Я начал копировать его подходы в архитектуре и написании кода, и это сработало: у меня начали получаться более безопасные, простые и качественные решения.
- Возьми проект, чтобы ты мог за него отвечать. Свой текущий проект на работе я делаю последние 3 года. Я считаю это одним из главных драйверов моей карьеры: с ним я вырос от мидла до ведущего, защитил диплом в универе, заонбордил в команду 3-ёх джунов и, спойлер, пойду выступать на SaintHighload++ в этом году. Продолжительный по времени проект развивает в тебе кучу компетенций “на вырост”: учит правильно выбирать архитектуру и нести ответственность за плохие решения, развивает навыки дебага, требует настройки observability и процесса QA. Все эти навыки со временем сделают из тебя сеньора.
- Бонус №1: преподавание. Преподавание помогает структурировать свои собственные знания, чтобы их было удобно передать другим. Попробуйте заонбордить в свой проект стажёра. Выступите в роли ментора для начинающего программиста. Подготовьте доклад/документацию по своему проекту для коллег. Выступите на митапе. Всё это отлично упорядочивает ваши знания и, в конечном итоге, позволяет учиться быстрее.
- Бонус №2: экспертиза в предметной области. Всю свою карьеру я построил в одной компании. Я работаю над Почтой Mail.ru, и она моя ровесница: Почту запустили в год моего рождения 😂. Когда работаешь над проектом с такой историей, на то, чтобы разобраться в нём, уходят годы. Это те знания, которые невозможно получить снаружи компании. И они становятся твоим конкурентным преимуществом: позволяют выполнять задачи быстрее и качественнее. Найди хорошую компанию и поработай в ней хотя бы 2-3 года - количество возможностей для роста сильно увеличится 😉
Senior → Lead. После сеньора остаётся не так много возможностей для роста. По сути есть два самых популярных трека: менеджерский (в тимлида) и экспертный (в ведущего разработчика). Экспертный трек - не самое частое явление, такая возможность есть не во всех компаниях.
- Если хочешь строить карьеру по экспертному треку, начинай делиться экспертизой. Тут преподавание превращается из бонуса в must have. Без этого будет крайне сложно убедить коллег и руководство в своей экспертности.
- Выбери специализацию. Сеньор может решать крайне широкий спектр задач и переход в экспертную роль этого не отнимает. Однако, чтобы выполнить такой переход эффективно, нужно сконцентрировать свои усилия на какой-то одной области. Такой подход позволит решать какой-то тип задач эффективнее всех остальных - это и называется экспертизой.
- Продолжай учиться. Ну уровне сеньора самое время начать потреблять хардкорный профессиональный контент. Это тот этап, когда профессиональные конференции и умные книжки становятся максимально полезными. Для мидла всё ещё важнее навыки практического решения задач, а вот сеньору может помочь вырасти понимание фундаментальных закономерностей и стандартных методологий.
Вот такой у меня карьерный трек. Я постарался изложить самые сильные драйверы роста, которые помогли мне двигаться по карьерной лестнице. Надеюсь, теперь они помогут тебе 🔥 А любые вопросы можешь задать в комменты 👇
#career #selflearning
Главный источник знаний в проекте
Этот маленький засранец знает ответы на самые сложные вопросы:
- Сколько тредов запускает приложение?
- На каких серверах запущен nginx?
- Какие сервисы ходят в эту БД?
Вот он, святой грааль разработчика:
Греп по сорцам - один из самых важных хардскиллов, которым я научился за 5 лет в VK. Он позволяет находить новые знания в текущей кодовой базе, без всякой документации, комментов и вопросов к старшим коллегам. Всего того, чего в большом проекте с историей может и не быть.
Grep - это семейство утилит для текстового поиска. Я чаще всего пользуюсь такими вариантами грепа, как:
-
-
-
Самые важные флаги grep-утилит (одинаковые для всех вариантов):
-
-
-
-
Всё это может звучать дико, но я действительно пользуюсь только этим видом поиска. Это потребовало практики, но результат того стоит: я могу самостоятельно найти ответы на любые вопросы по проекту без временных затрат на коммуникацию с коллегами. Это грандиозно повышает производительность. Методология примерно такая:
0️⃣ Начинаем с вопроса: что мы хотим найти? Например, “кто ходит в сервис А” или “сколько соединений открывает сервис Б”. Далее, выбираем область поиска. Если твой вопрос по внутреннему устройству какого-то софта, ищем по его исходному коду. Если твой вопрос про конфигурацию (взаимодействие между сервисами или продакшен-настройки), ищем по IaC репозиторию, если у твоей компании он есть. Например, мы используем Puppet и Kubernetes, многие пользуются Ansible. Не стесняйтесь отсекать директории, в которых точно нет ничего полезного. Например, искать по
1️⃣ Дальше выполняем рекурсивный поиск. Я убеждён, что самое главное в этом вопросе - контроль фокуса. Мы не хотим найти слишком много результатов, глаз будет замыливаться и можно пропустить что-то важное. Поэтому начинаем с самого простого
2️⃣ Если ничего не нашли, можем расширять поисковый запрос. Добавить флаг
3️⃣ Скорее всего, после расширения запроса в поисковую выдачу попадёт что-то лишнее. Не стесняемся добавлять
4️⃣ В конце концов, ты либо найдёшь, что ищешь, либо сдашься и пойдёшь спрашивать помощи у коллег. Второй вариант на самом деле неплохой, но тут тоже есть один трюк. Скорее всего, коллега, который ответит тебе на вопрос, тоже этот ответ как-то найдёт. Невозможно запомнить все нюансы и тонкости в большой системе, но возможно научиться находить эту информацию. И трюк тут в том, чтобы после ответа на свой вопрос спросить: “А как ты это нашёл?” Это гораздо важнее, чем сам ответ, потому что позволит развить навыки поиска информации и, в конченом итоге, повысить самостоятельность.
Этот простой алгоритм, когда я был джуном, стал мощным драйвером роста моих навыков, и я им пользуюсь до сих пор. В следующий раз, когда у тебя возникнет вопрос по проекту, не спеши обращаться к коллегам, попробуй найти ответ сам! Ну и
#hardskills #codereading #selflearning #Linux #tools
Этот маленький засранец знает ответы на самые сложные вопросы:
- Сколько тредов запускает приложение?
- На каких серверах запущен nginx?
- Какие сервисы ходят в эту БД?
Вот он, святой грааль разработчика:
fgrep -rie "pthread_create" ./src
Греп по сорцам - один из самых важных хардскиллов, которым я научился за 5 лет в VK. Он позволяет находить новые знания в текущей кодовой базе, без всякой документации, комментов и вопросов к старшим коллегам. Всего того, чего в большом проекте с историей может и не быть.
Grep - это семейство утилит для текстового поиска. Я чаще всего пользуюсь такими вариантами грепа, как:
-
fgrep
- простой текстовый поиск, никакие символы в поисковом запросе не имеют специального значения-
grep
- текстовый поиск по регулярным выражениям-
zgrep
/ zfgrep
- всё то же самое, но позволяет искать по сжатым gzip-файлам без необходимости полной декомпрессии. Используется редко, но бывает полезно для поиска по историческим логам.Самые важные флаги grep-утилит (одинаковые для всех вариантов):
-
-r
- рекурсивный поиск по директории, именно этот флаг позволяет искать “по всему проекту” или “по какой-то части проекта”.-
-i
- включает case-insensitive поиск (это когда заглавные и строчные буквы интерпретируются одинаково, т.е. между “А” и “а” нет никакой разницы).-
-e
- включает extended regexp syntax, я уже привык к этому синтаксису, поэтому включаю этот флаг всегда по привычке. Для fgrep
он ничего не делает.-
-v
- исключающий поиск, это когда мы ищем “всё, кроме <поисковый запрос>”Всё это может звучать дико, но я действительно пользуюсь только этим видом поиска. Это потребовало практики, но результат того стоит: я могу самостоятельно найти ответы на любые вопросы по проекту без временных затрат на коммуникацию с коллегами. Это грандиозно повышает производительность. Методология примерно такая:
0️⃣ Начинаем с вопроса: что мы хотим найти? Например, “кто ходит в сервис А” или “сколько соединений открывает сервис Б”. Далее, выбираем область поиска. Если твой вопрос по внутреннему устройству какого-то софта, ищем по его исходному коду. Если твой вопрос про конфигурацию (взаимодействие между сервисами или продакшен-настройки), ищем по IaC репозиторию, если у твоей компании он есть. Например, мы используем Puppet и Kubernetes, многие пользуются Ansible. Не стесняйтесь отсекать директории, в которых точно нет ничего полезного. Например, искать по
./src
вместо .
, чтобы не заходить в ./third_party
, ./vendor
или ./node_modules
- это сильно ускоряет процесс.1️⃣ Дальше выполняем рекурсивный поиск. Я убеждён, что самое главное в этом вопросе - контроль фокуса. Мы не хотим найти слишком много результатов, глаз будет замыливаться и можно пропустить что-то важное. Поэтому начинаем с самого простого
fgrep -re
2️⃣ Если ничего не нашли, можем расширять поисковый запрос. Добавить флаг
-i
или перейти на grep
и поиск по регулярке.3️⃣ Скорее всего, после расширения запроса в поисковую выдачу попадёт что-то лишнее. Не стесняемся добавлять
| grep -v
в конец запроса и отсекать ненужные варианты.4️⃣ В конце концов, ты либо найдёшь, что ищешь, либо сдашься и пойдёшь спрашивать помощи у коллег. Второй вариант на самом деле неплохой, но тут тоже есть один трюк. Скорее всего, коллега, который ответит тебе на вопрос, тоже этот ответ как-то найдёт. Невозможно запомнить все нюансы и тонкости в большой системе, но возможно научиться находить эту информацию. И трюк тут в том, чтобы после ответа на свой вопрос спросить: “А как ты это нашёл?” Это гораздо важнее, чем сам ответ, потому что позволит развить навыки поиска информации и, в конченом итоге, повысить самостоятельность.
Этот простой алгоритм, когда я был джуном, стал мощным драйвером роста моих навыков, и я им пользуюсь до сих пор. В следующий раз, когда у тебя возникнет вопрос по проекту, не спеши обращаться к коллегам, попробуй найти ответ сам! Ну и
grep
в помощь, куда же без него 🔥#hardskills #codereading #selflearning #Linux #tools