Когда мы проводим собеседование, то ориентируемся на правило: кандидат должен знать 3 из 5 (или 5 из 7, что зависит от позиции) ключевых для нас технологий. Казалось бы, если кандидат сильный, то он должен быстро вникнуть в новые для него технологии и начать перформить (шлёпать бизнес-тасочки). Так и есть, но сильные кандидаты приходят не так часто (в этом случае мы можем закрыть на правило глаза). К сожалению, для слабых не проактивных кандидатов это становится серьёзным барьером для того, чтобы пройти испытательный срок. Времени на раскачку нет: бизнес хочет получать новые фичи как можно раньше, очень желательно уже с первого рабочего дня.
В последний год на собеседования стало приходить много кандидатов, которые совсем не знакомы с Unix/Linux. С чем это связано? Видимо, с тем, что курсы вкатунов не дают эту базу. А нужно ли ее знать? Часто в компаниях есть команда девопсов, которая сделает всю грязную работу за нас. Но так бывает далеко не всегда.
Unix/Linux — это отдельный пласт теории и практики в IT требующий знания ОС, сетей и администрирования. Важной главой в изучении Linux является знакомство с оболочкой операционной системы (от англ. shell «оболочка»). Это интерпретатор команд, обеспечивающий интерфейс для взаимодействия пользователя с функциями системы. Наиболее популярная оболочка в мире Linux — bash.
Научиться основам работы с bash не так сложно, но всё же для уверенной работы нужно некоторое время. Предлагаю протестировать свои знания по теме командных оболочек и bash в частности по чеклисту, который представлен ниже. Проверьте, что знаете:
* про существование разных оболочек: bash, zsh, sh и других
* типы командных оболочек: interactive vs non-interactive, login vs non-login
* для чего нужны файлы /etc/profile, /etc/bash.bashrc, ~/.bashrc, ~/.bashrc, ~/.profile и ~/.bash_logout
* про переменные окружения
* команды для работы с файловой системой (pwd, ls, cd, touch, mkdir, rm, rmdir, cp, mv, ln) и с файлами (cat, grep, head, tail, wc, tar, less, find, sed, awk)
* потоки ввода-вывода
* как написать простой скрипт с условиями, циклами
Если вы еще не знакомы с bash или чувствуете пробелы в знаниях, то рекомендую ознакомиться с серией переводов https://habr.com/ru/companies/ruvds/articles/325522/. Если хотите отточить свои знания на практике, то заходите в личку @senior_junior_dev за практическими заданиями.
В последний год на собеседования стало приходить много кандидатов, которые совсем не знакомы с Unix/Linux. С чем это связано? Видимо, с тем, что курсы вкатунов не дают эту базу. А нужно ли ее знать? Часто в компаниях есть команда девопсов, которая сделает всю грязную работу за нас. Но так бывает далеко не всегда.
Unix/Linux — это отдельный пласт теории и практики в IT требующий знания ОС, сетей и администрирования. Важной главой в изучении Linux является знакомство с оболочкой операционной системы (от англ. shell «оболочка»). Это интерпретатор команд, обеспечивающий интерфейс для взаимодействия пользователя с функциями системы. Наиболее популярная оболочка в мире Linux — bash.
Научиться основам работы с bash не так сложно, но всё же для уверенной работы нужно некоторое время. Предлагаю протестировать свои знания по теме командных оболочек и bash в частности по чеклисту, который представлен ниже. Проверьте, что знаете:
* про существование разных оболочек: bash, zsh, sh и других
* типы командных оболочек: interactive vs non-interactive, login vs non-login
* для чего нужны файлы /etc/profile, /etc/bash.bashrc, ~/.bashrc, ~/.bashrc, ~/.profile и ~/.bash_logout
* про переменные окружения
* команды для работы с файловой системой (pwd, ls, cd, touch, mkdir, rm, rmdir, cp, mv, ln) и с файлами (cat, grep, head, tail, wc, tar, less, find, sed, awk)
* потоки ввода-вывода
* как написать простой скрипт с условиями, циклами
Если вы еще не знакомы с bash или чувствуете пробелы в знаниях, то рекомендую ознакомиться с серией переводов https://habr.com/ru/companies/ruvds/articles/325522/. Если хотите отточить свои знания на практике, то заходите в личку @senior_junior_dev за практическими заданиями.
👍5👀2
Всю прошлую неделю для оттачивания навыка написания скриптов на bash я разрабатывал скрипт, который выдает информацию об утилизации ресурсов системы (cpu, mem, iops, network).
Что было на входе?
0) техническое задание примерно на 2 страницы A4
1) слабые знания синтаксиса и семантики конструкций в bash
2) отсутствие опыта написания длинных bash-скриптов
3) желание написать скрипт в разумные сроки
Я снова почувствовал себя студентом, у которого нет времени глубоко разобраться в вопросе, а сдавать лабораторные надо и побыстрее. Начинать чтение какой-то серии статей или книги у меня не было никакого желания, ровно как и смотреть длинные обучающие видео на YT. Мне не так часто приходится работать с bash, поэтому фундаментальные знания мне не особенно нужны, а многие нюансы через время забудутся.
Какие у меня были варианты? Последовательное выполнение пунктов огромного технического задания заняло бы слишком много времени. Однако мне нужно было с чего-то начать. Думаю, что исходя из прошлых постов, многие догадались, какой был мой первый шаг. Конечно, я скормил техническое задание ChatGPT. На выходе я получил вполне качественную болванку, которая стала точкой отсчета. Почему только болванку? Скрипт не был полностью работоспособным и не все нюансы технического задания были учтены.
В течение следующих нескольких дней я задал 46 вопросов для уточнения синтаксиса, наращивания функциональности. Некоторые предлагаемые решения содержали ошибки и неточности. Наиболее частые из них:
0) некорректная работа с awk
1) некорректная работа со сдвигами аргументов (в частности использование shift внутри функции приводит к сдвигу только аргументов в контексте функции и не влияет на переданные пользователем аргументы в скрипт);
2) некорректная работа с глобальными переменными и возвращаемыми значениями (например, при возвращении значения из функции через echo теряются изменения глобальных переменных).
С этими ошибками мне приходилось бороться постоянно, так как они "забывались" из контекста чата между моими вопросами. Приходилось делать постоянные отсылки к правильности использования этих команд.
Что в итоге? Еще раз подтверждаю, что ChatGPT — очень крутой инструмент в руках опытного разработчика. Это как коллега, который может мгновенно ответить на любой твой вопрос и всегда онлайн. В один из дней ChatGPT работал плохо и моя производительность сильно снизилась. Я чувствовал свою беспомощность, так как некоторые вопросы было не так просто сформулировать гуглу (в этом заключается отдельная сила опытного разработчика). Представляю сейчас новичков в программировании, которые при изучении языка подсели на иглу ChatGPT и с которой при всем желании уже не слезут.
И еще раз убеждаюсь, что чуда не случилось. Мне все равно пришлось изучить синтаксис и работу конструкций bash, а также предназначение, флаги и выводы команд. В решении нужно много времени уделять унификации стиля кода и рефакторингу, так как получающаяся при таком подходе к разработке программа похожа на лоскутное одеяло. Но стартовать не с нуля было гораздо приятнее, а прогресс шел стремительными темпами. Мне потребовалось около 12-16 часов (может больше, может меньше, точно не замерял 🙈), чтобы получить устраивающее меня рабочее решение, в котором однако есть масса возможностей для улучшения.
Предлагаю посмотреть, с чего я начал свой путь и к чему пришел здесь.
Что было на входе?
0) техническое задание примерно на 2 страницы A4
1) слабые знания синтаксиса и семантики конструкций в bash
2) отсутствие опыта написания длинных bash-скриптов
3) желание написать скрипт в разумные сроки
Я снова почувствовал себя студентом, у которого нет времени глубоко разобраться в вопросе, а сдавать лабораторные надо и побыстрее. Начинать чтение какой-то серии статей или книги у меня не было никакого желания, ровно как и смотреть длинные обучающие видео на YT. Мне не так часто приходится работать с bash, поэтому фундаментальные знания мне не особенно нужны, а многие нюансы через время забудутся.
Какие у меня были варианты? Последовательное выполнение пунктов огромного технического задания заняло бы слишком много времени. Однако мне нужно было с чего-то начать. Думаю, что исходя из прошлых постов, многие догадались, какой был мой первый шаг. Конечно, я скормил техническое задание ChatGPT. На выходе я получил вполне качественную болванку, которая стала точкой отсчета. Почему только болванку? Скрипт не был полностью работоспособным и не все нюансы технического задания были учтены.
В течение следующих нескольких дней я задал 46 вопросов для уточнения синтаксиса, наращивания функциональности. Некоторые предлагаемые решения содержали ошибки и неточности. Наиболее частые из них:
0) некорректная работа с awk
1) некорректная работа со сдвигами аргументов (в частности использование shift внутри функции приводит к сдвигу только аргументов в контексте функции и не влияет на переданные пользователем аргументы в скрипт);
2) некорректная работа с глобальными переменными и возвращаемыми значениями (например, при возвращении значения из функции через echo теряются изменения глобальных переменных).
С этими ошибками мне приходилось бороться постоянно, так как они "забывались" из контекста чата между моими вопросами. Приходилось делать постоянные отсылки к правильности использования этих команд.
Что в итоге? Еще раз подтверждаю, что ChatGPT — очень крутой инструмент в руках опытного разработчика. Это как коллега, который может мгновенно ответить на любой твой вопрос и всегда онлайн. В один из дней ChatGPT работал плохо и моя производительность сильно снизилась. Я чувствовал свою беспомощность, так как некоторые вопросы было не так просто сформулировать гуглу (в этом заключается отдельная сила опытного разработчика). Представляю сейчас новичков в программировании, которые при изучении языка подсели на иглу ChatGPT и с которой при всем желании уже не слезут.
И еще раз убеждаюсь, что чуда не случилось. Мне все равно пришлось изучить синтаксис и работу конструкций bash, а также предназначение, флаги и выводы команд. В решении нужно много времени уделять унификации стиля кода и рефакторингу, так как получающаяся при таком подходе к разработке программа похожа на лоскутное одеяло. Но стартовать не с нуля было гораздо приятнее, а прогресс шел стремительными темпами. Мне потребовалось около 12-16 часов (может больше, может меньше, точно не замерял 🙈), чтобы получить устраивающее меня рабочее решение, в котором однако есть масса возможностей для улучшения.
Предлагаю посмотреть, с чего я начал свой путь и к чему пришел здесь.
Gist
Результаты написания bash-скрипта
Результаты написания bash-скрипта. GitHub Gist: instantly share code, notes, and snippets.
👍7
Давайте рассмотрим следующую ситуацию с точки зрения поиска работы. На собеседовании вам рассказывают следующее: "Мы работаем по принципу 80/20. В нашем подразделении есть договоренность с бизнесом, что 20% времени разработчика уходит на технические задачи по выбору разработчика, которые нужны, чтобы поддерживать кластер в добром здравии (обновление библиотек, инфраструктуры и т.п). Задачи фиксируются в начале квартала, в конце квартала за них нужно отчитаться."
Перед тем, как я расскажу, как это работает у нас, попробуйте спрогнозировать, как это будет влиять на вашу работу, если вы выберите эту компанию.
Звучит просто волшебно: есть один в день неделю, который можно тратить на что-то интересное, да ещё и система в легаси скатываться не будет. В теории все гладко, но потом она встречается с жестокой реальностью.
1) не у всех разработчиков высокая дисциплина (а у вас?). Выделить время на технические задачи во время спринта, а не лежать на диване и смотреть ютуб удается далеко не всем;
2) бизнес только и ждет, чтобы откусить лакомый кусочек технического времени (тесты и документация тоже потенциальные жертвы). Зачем тратить время на техтаски, если можно запросить очередную сверхважную фичу, которую надо было увидеть на проде ещё вчера? И не важно, что это время на дистанции как раз снижает TTM для фич бизнеса;
3) задачи коммитятся перед ИТ-лидером (некто между CTO и техлидами), цели которого могут расходиться с теми задачами, которые вы хотите (вам нужно) сделать;
4) на фоне предыдущих поинтов разработчикам сложно брать на себя амбициозные задачи, так как успешность их выполнения зависит не только от них.
Это только один из возможных сценариев, по которому могут развиваться события. Если вам не хочется быть заложником обстоятельств, то на собеседовании уточняйте про существование и порядок работы с техтасками у того, кто будет вашим непосредственным начальником. Задокументируйте их и в будущем ссылайтесь, если начнутся расхождения обещаний с действительностью. Будьте готовы отстаивать свою позицию с первого дня, не прогибаясь под сиюминутные желания бизнеса (как вы понимаете, речь не только о техтасках).
Перед тем, как я расскажу, как это работает у нас, попробуйте спрогнозировать, как это будет влиять на вашу работу, если вы выберите эту компанию.
Звучит просто волшебно: есть один в день неделю, который можно тратить на что-то интересное, да ещё и система в легаси скатываться не будет. В теории все гладко, но потом она встречается с жестокой реальностью.
1) не у всех разработчиков высокая дисциплина (а у вас?). Выделить время на технические задачи во время спринта, а не лежать на диване и смотреть ютуб удается далеко не всем;
2) бизнес только и ждет, чтобы откусить лакомый кусочек технического времени (тесты и документация тоже потенциальные жертвы). Зачем тратить время на техтаски, если можно запросить очередную сверхважную фичу, которую надо было увидеть на проде ещё вчера? И не важно, что это время на дистанции как раз снижает TTM для фич бизнеса;
3) задачи коммитятся перед ИТ-лидером (некто между CTO и техлидами), цели которого могут расходиться с теми задачами, которые вы хотите (вам нужно) сделать;
4) на фоне предыдущих поинтов разработчикам сложно брать на себя амбициозные задачи, так как успешность их выполнения зависит не только от них.
Это только один из возможных сценариев, по которому могут развиваться события. Если вам не хочется быть заложником обстоятельств, то на собеседовании уточняйте про существование и порядок работы с техтасками у того, кто будет вашим непосредственным начальником. Задокументируйте их и в будущем ссылайтесь, если начнутся расхождения обещаний с действительностью. Будьте готовы отстаивать свою позицию с первого дня, не прогибаясь под сиюминутные желания бизнеса (как вы понимаете, речь не только о техтасках).
✍3😢1
На горизонте маячит Java 23, которая на данный момент предлагает всего один JEP (JDK Enhancement Proposal), а именно Primitive Types in Patterns, instanceof, and switch (Preview). Если в Kotlin решили отказаться от использования примитивов (на уровне исходных кодов), то Java вынуждена поддерживать обратную совместимость, от которой, кажется, можно было бы давно отказаться (тогда с Java 6/7/8 легаси проекты точно не уйдут никогда). Но приходится довольствоваться малым.
Конечно, в последних версиях Java значительно расширились поддержка pattern matching, но на фоне возможностей, которые предоставляют другие языки, это выглядит неубедительно.
Давайте посмотрим на следующий простой пример на F#:
В этом примере sumPositive — это рекурсивная функция, которая принимает список чисел. Мы используем pattern matching для разбора списка на голову (head) и хвост (tail):
* Если список пуст ([]), возвращаем 0.
* Если голова списка (head) положительна (when head > 0), добавляем её к результату рекурсивного вызова функции с хвостом списка.
* Если голова списка не положительна, продолжаем рекурсию с хвостом, не добавляя голову к сумме.
Ну согласитесь, красиво же? Вот еще пример. Напишем на F# код для генерации всех подмножеств заданного размера из множества целых чисел от 1 до n.
Поиграться можно здесь.
Ждем, когда Java перестанет быть догоняющей в этой бесконечной гонке или перейдем на Kotlin окончательно 😄
Конечно, в последних версиях Java значительно расширились поддержка pattern matching, но на фоне возможностей, которые предоставляют другие языки, это выглядит неубедительно.
Давайте посмотрим на следующий простой пример на F#:
let rec sumPositive list =
match list with
| [] -> 0
| head :: tail when head > 0 -> head + sumPositive tail
| _ :: tail -> sumPositive tail
let myList = [1; -5; 3; -2; 4]
let result = sumPositive myList
printfn "Sum of positive numbers: %d" resultВ этом примере sumPositive — это рекурсивная функция, которая принимает список чисел. Мы используем pattern matching для разбора списка на голову (head) и хвост (tail):
* Если список пуст ([]), возвращаем 0.
* Если голова списка (head) положительна (when head > 0), добавляем её к результату рекурсивного вызова функции с хвостом списка.
* Если голова списка не положительна, продолжаем рекурсию с хвостом, не добавляя голову к сумме.
Ну согласитесь, красиво же? Вот еще пример. Напишем на F# код для генерации всех подмножеств заданного размера из множества целых чисел от 1 до n.
let rec combine k list =
match k, list with
| _, [] -> []
| 0, _ -> [[]]
| k, x::xs ->
(combine k xs) @ (List.map (fun l -> x::l) (combine (k-1) xs))
let allSubsetsOfSize n k =
combine k [1..n]
let subsets = allSubsetsOfSize 5 3
printfn "%A" subsetsПоиграться можно здесь.
Ждем, когда Java перестанет быть догоняющей в этой бесконечной гонке или перейдем на Kotlin окончательно 😄
👍3❤1
А вам не кажется, что многие книги пишутся по принципу "кто понял, тот поймёт"?
Я прочитал книгу справа, когда уже имел за плечами опыт работы с Kafka, уже знал как она работает и устроена под капотом. Меня не покидало стойкое ощущение, что если бы я тему не знал, то по книге в ней бы не разобрался. Это при том, что темы затронуты достаточно базовые.
Я прочитал книгу справа, когда уже имел за плечами опыт работы с Kafka, уже знал как она работает и устроена под капотом. Меня не покидало стойкое ощущение, что если бы я тему не знал, то по книге в ней бы не разобрался. Это при том, что темы затронуты достаточно базовые.
👍2😁2
Конечно, немалую роль играет качество перевода. В этой книге (как и в многих других) он, мягко говоря, страдает. Мне читать техническую литературу на английском дается значительно сложнее, но выхлоп от такого чтения больше. Почему?
1) мысли автора переданы в том виде, в котором он их задумал. Часто при переводе теряются неуловимые смыслы, которые автор вкладывал в текст. Часто перевод можно сравнить с водой из фильтра обратного осмоса, когда на выходе получаем практически стерильную воду, лишенную какого-либо вкуса из-за отсутствия микроэлементов. Вроде суть та же, но уже не то. Про коверканье терминологии даже не говорю.
2) чтение получается более вдумчивым, так как иногда приходится расшифровывать смысл написанного. Дело даже не в том, что используются непонятные термины, а в построении фраз, когда некоторые из них можно трактовать двояко. Если вы бегло читаете техническую литературу на английском, то такого эффекта, вероятно, можете и не испытывать или испытывать, но не так ярко.
Ну и как тогда с Kafka разбираться, если даже книга апологета Kafka в русскоязычном комьюнити не очень. Я смотрел видеокурсы и практиковался на локально поднятом кластере Docker Compose. Что рекомендую? APACHE KAFKA 101 от Confluent (там еще много других курсов) и теория и практика от Conduktor Kafkademy.
После того, как я познакомился с базой — перешел к чтению книг и документации. Кстати, второе издание книги слева на английском языке мне показалось очень удачным: были даны ответы на многие вопросы, которые не мог найти где-либо еще.
А вам вообще Kafka интересна? Давайте соберемся на следующей неделе, например, вечером в среду (14 февраля) в 18:00 по МСК, пообщаемся на эту и смежные темы. Интересующие вас вопросы присылайте в личку @senior_junior_dev. Если конкретных вопросов не будет, то пробежимся по основным вопросам собеседований по Kafka. В общем без качественного контента я вас не оставлю.
1) мысли автора переданы в том виде, в котором он их задумал. Часто при переводе теряются неуловимые смыслы, которые автор вкладывал в текст. Часто перевод можно сравнить с водой из фильтра обратного осмоса, когда на выходе получаем практически стерильную воду, лишенную какого-либо вкуса из-за отсутствия микроэлементов. Вроде суть та же, но уже не то. Про коверканье терминологии даже не говорю.
2) чтение получается более вдумчивым, так как иногда приходится расшифровывать смысл написанного. Дело даже не в том, что используются непонятные термины, а в построении фраз, когда некоторые из них можно трактовать двояко. Если вы бегло читаете техническую литературу на английском, то такого эффекта, вероятно, можете и не испытывать или испытывать, но не так ярко.
Ну и как тогда с Kafka разбираться, если даже книга апологета Kafka в русскоязычном комьюнити не очень. Я смотрел видеокурсы и практиковался на локально поднятом кластере Docker Compose. Что рекомендую? APACHE KAFKA 101 от Confluent (там еще много других курсов) и теория и практика от Conduktor Kafkademy.
После того, как я познакомился с базой — перешел к чтению книг и документации. Кстати, второе издание книги слева на английском языке мне показалось очень удачным: были даны ответы на многие вопросы, которые не мог найти где-либо еще.
А вам вообще Kafka интересна? Давайте соберемся на следующей неделе, например, вечером в среду (14 февраля) в 18:00 по МСК, пообщаемся на эту и смежные темы. Интересующие вас вопросы присылайте в личку @senior_junior_dev. Если конкретных вопросов не будет, то пробежимся по основным вопросам собеседований по Kafka. В общем без качественного контента я вас не оставлю.
👍5🔥2
Во-первых, Kafka — кормилица. Такое выражение я придумал после того, как изучил Kafka и начал ходить по собесам. Все, вплоть до архитекторов удивлялись моим знаниям. Прям удивляются? Да! Почему? Видимо, распространенность технологии и знания о ней массы разработчиков не коррелируют. Я чувствовал, что набираю много очков при ответе на каверзные по мнению интервьюера вопросы. Осталось еще MongoDB и PostgreSQL довести до того же уровня 😈
Вообще практически на любом собесе речь рано или поздно заходит про Kafka. Ну или у меня уже появился особый навык сводить все разговоры к этой технологии 😂 Ну а раз есть чем хвастаться, то нужно это делать 🤓
Во-вторых, Kafka — это кладезь знаний. Только посмотрите на Kafka Improvement Proposals (KIP). Что такое KIP? Это идеи по изменению архитектуры/функциональности Kafka. Некоторые уже воплощены в жизнь, а некоторые только предстоит. Каждый из KIP решает определенную проблему/задачу. Очень ценно, что эти изменения происходят в реальном продукте, пользующемся огромной популярность. Кстати, очень много идей можно почерпнуть, изучив устройство хотя бы минимально отказоустойчивого кластера и то, как взаимодействуют с таким кластером клиенты.
Если начать читать все подряд пока нет решимости, то здесь, например, приведен обзор некоторый KIP`ов по улучшению производительности. Меня в свое время набор этих техник очень впечатлил.
Мой друг, Антон, постоянно спотыкается на собеседованиях на вопросах по Kafka. Буквально сегодня он получил обратную связь от Самоката (на скрине выше). Не будьте как Антон. Давайте сделаем шаг в светлое будущее, чтобы получать офферы в компании мечты. Как и обещал на сегодняшнем вебинаре разберем частые вопросы собеседований. Заходите по ссылке в 19:00 по МСК. Если будут вопросы, то пишите в личку @senior_junior_dev .
Антон, кстати, сказал, что не сможет прийти, у него танцы 😄.
Вообще практически на любом собесе речь рано или поздно заходит про Kafka. Ну или у меня уже появился особый навык сводить все разговоры к этой технологии 😂 Ну а раз есть чем хвастаться, то нужно это делать 🤓
Во-вторых, Kafka — это кладезь знаний. Только посмотрите на Kafka Improvement Proposals (KIP). Что такое KIP? Это идеи по изменению архитектуры/функциональности Kafka. Некоторые уже воплощены в жизнь, а некоторые только предстоит. Каждый из KIP решает определенную проблему/задачу. Очень ценно, что эти изменения происходят в реальном продукте, пользующемся огромной популярность. Кстати, очень много идей можно почерпнуть, изучив устройство хотя бы минимально отказоустойчивого кластера и то, как взаимодействуют с таким кластером клиенты.
Если начать читать все подряд пока нет решимости, то здесь, например, приведен обзор некоторый KIP`ов по улучшению производительности. Меня в свое время набор этих техник очень впечатлил.
Мой друг, Антон, постоянно спотыкается на собеседованиях на вопросах по Kafka. Буквально сегодня он получил обратную связь от Самоката (на скрине выше). Не будьте как Антон. Давайте сделаем шаг в светлое будущее, чтобы получать офферы в компании мечты. Как и обещал на сегодняшнем вебинаре разберем частые вопросы собеседований. Заходите по ссылке в 19:00 по МСК. Если будут вопросы, то пишите в личку @senior_junior_dev .
Антон, кстати, сказал, что не сможет прийти, у него танцы 😄.
Хабр
Почему Kafka такая быстрая
За последние несколько лет в сфере архитектуры ПО произошли огромные изменения. Идея единственного монолитного приложения или даже нескольких крупных сервисов, разделяющих общий массив данных,...
👍3🔥2
На днях со мной созвонился коллега, js-разработчик (React), по вопросу профессионального развития. Его план выглядел следующим образом: изучить Java, чтобы стать fullstack-разработчиком и начать делать задачи в рамках текущей бизнесовой команды. Я постарался отговорить его от этой затеи, хотя в ней есть здравое зерно. Звучит противоречиво? Сейчас расскажу, почему так думаю.
В коммерческих проектах роль fullstack-разработчика я не занимал, но успел в течение полугода разрабатывать фронт для портала недвижимости на Angular в команде из 30 человек. Само по себе такое приключение было для меня, безусловно, полезно. Я познакомился с новыми для себя языком, процессами и инструментами, однако, твердо для себя решил, что fullstack не мое и стал глубже погружаться в дебри бэкенда.
Не то, что бы фронт-разработка мне была не интересна, просто я ощущал, что буду распылять свои усилия на две достаточно большие области. Кажется, что fullstack-разработчик выгоден только бизнесу, а сам такой разработчик не получает от своей универсальности больших бенефитов (только если не хочется сделать свой коммерческий проект от и до).
Сами посудите, сплошная оптимизация:
1) не нужно нанимать двух людей, когда один со всем справится, а судя по hh золотые горы таким универсалам не сулят -> экономия на зарплате
2) fullstack-разработчик испытывает меньше переключений контекста, так как выполняет более объемные и комплексные задачи -> может сделать больший объем полезной работы
3) постановка задач значительно упрощается, так как fullstack-разработчик знает процессы, контракты, ограничения (и т.д.) и на бэке и фронте -> экономим ресурс аналитика
4) отпадает необходимость синхронизации бэка с фронтом, да и ошибки на стыке технологий решаются быстрее -> убиваем сразу двух зайцев: и ресурсы экономим и ответственность за ошибки выше (на дискоммуникацию все свалить не выйдет)
Потратить время на изучение Java для дальнейшего решения бизнесовых задач — вполне понятная дорога, которая вряд ли выведет вашу карьеру на принципиально новый уровень. Java или любой другой язык бэкенда изучить все же нужно, но не для fullstack-разработки для бизнеса, а для расширения кругозора, познания новых техник и практик. Примерно так, как я когда-то сам изучил js и Angular. Какой сделать следующий шаг? Конечно, погружаться глубже в уже известные языки и инструменты. А дальше?
Дальше предлагаю пойти не по пути мейнстрима, а углубиться в Computer Science, чтобы сформировать крутой фундамент и начать делать реально крутые вещи в индустрии. Кажется в обилии курсов и книг по конкретным технологиям, эта область ушла куда-то на третий план. Вот отличный (хотя и не полный) список книг преимущественно по Computer Science. Последние несколько месяцев как раз изучаю избранные главы книги Object Oriented Software Construction Бертрана Мейера.
Наверное, еще пару лет назад я бы не дал такой совет, но сытые деньки могут скоро закончиться: LLM с каждой неделей показывают все большие успехи. По моему мнению в зоне максимального риска находятся как наименее обученный персонал (junior-разработчики), так и поверхностные разработчики-универсалы. Все же знают, что ChatGPT решает джуниорские задачи на ура? Поэтому предлагаю качать базу, копать в глубину, а не поверхностно вникать в очередную популярную технологию особенно в угоду бизнесу.
В коммерческих проектах роль fullstack-разработчика я не занимал, но успел в течение полугода разрабатывать фронт для портала недвижимости на Angular в команде из 30 человек. Само по себе такое приключение было для меня, безусловно, полезно. Я познакомился с новыми для себя языком, процессами и инструментами, однако, твердо для себя решил, что fullstack не мое и стал глубже погружаться в дебри бэкенда.
Не то, что бы фронт-разработка мне была не интересна, просто я ощущал, что буду распылять свои усилия на две достаточно большие области. Кажется, что fullstack-разработчик выгоден только бизнесу, а сам такой разработчик не получает от своей универсальности больших бенефитов (только если не хочется сделать свой коммерческий проект от и до).
Сами посудите, сплошная оптимизация:
1) не нужно нанимать двух людей, когда один со всем справится, а судя по hh золотые горы таким универсалам не сулят -> экономия на зарплате
2) fullstack-разработчик испытывает меньше переключений контекста, так как выполняет более объемные и комплексные задачи -> может сделать больший объем полезной работы
3) постановка задач значительно упрощается, так как fullstack-разработчик знает процессы, контракты, ограничения (и т.д.) и на бэке и фронте -> экономим ресурс аналитика
4) отпадает необходимость синхронизации бэка с фронтом, да и ошибки на стыке технологий решаются быстрее -> убиваем сразу двух зайцев: и ресурсы экономим и ответственность за ошибки выше (на дискоммуникацию все свалить не выйдет)
Потратить время на изучение Java для дальнейшего решения бизнесовых задач — вполне понятная дорога, которая вряд ли выведет вашу карьеру на принципиально новый уровень. Java или любой другой язык бэкенда изучить все же нужно, но не для fullstack-разработки для бизнеса, а для расширения кругозора, познания новых техник и практик. Примерно так, как я когда-то сам изучил js и Angular. Какой сделать следующий шаг? Конечно, погружаться глубже в уже известные языки и инструменты. А дальше?
Дальше предлагаю пойти не по пути мейнстрима, а углубиться в Computer Science, чтобы сформировать крутой фундамент и начать делать реально крутые вещи в индустрии. Кажется в обилии курсов и книг по конкретным технологиям, эта область ушла куда-то на третий план. Вот отличный (хотя и не полный) список книг преимущественно по Computer Science. Последние несколько месяцев как раз изучаю избранные главы книги Object Oriented Software Construction Бертрана Мейера.
Наверное, еще пару лет назад я бы не дал такой совет, но сытые деньки могут скоро закончиться: LLM с каждой неделей показывают все большие успехи. По моему мнению в зоне максимального риска находятся как наименее обученный персонал (junior-разработчики), так и поверхностные разработчики-универсалы. Все же знают, что ChatGPT решает джуниорские задачи на ура? Поэтому предлагаю качать базу, копать в глубину, а не поверхностно вникать в очередную популярную технологию особенно в угоду бизнесу.
👍12❤1❤🔥1
В качестве развлекательного контента на ночь посмотрел обзорный курс по NoSQL, который является в некотором роде реинкарнацией книги Семь баз данных за семь недель. Кажется, что только ленивый не оставил язвительный комментарий, имея в виду несколько провокационный, а значит рабочий, заголовок. Кстати, вышло второе издание книги, которое с натяжкой можно считать актуальной. Мне по душе такой формат, когда не хочется тратить много времени на изучение не нужной в момент темы, но хочется быстро расширить кругозор. Но сейчас не об этом.
Когда речь заходит о NoSQL, то чаще всего вспоминают о CAP-теореме, так как она описывает, какими свойствами обладают распределенные системы. Мы можем быстро окрестить ту или иную технологию CA, AP, CP, тем самым внести ясность для всех участников дискуссии, например, при выборе очередной БД для проекта 🧐 На самом деле сфера применения CAP-теоремы гораздо уже, чем это может показаться на первый взгляд. Предлагаю разобраться, в чем собственно суть теоремы, что значит каждая из букв в этой аббревиатуре, когда её можно применять, а когда — нет.
Несколько удивительно, что трактовку CAP-теоремы помнят абсолютно все, а вот значения понятий, которыми она оперирует, достаточно сильно искажают (хотя в английской статье на Википедии трактовка дана вполне корректная. А ведь достаточно просто обратиться к первоисточнику, что мы с вами и сделаем.
В соответствии с документом в контексте распределенных систем рассматриваются три свойства:
* Consistency — консистентность данных в системе;
* Availability — доступность узлов системы;
* Partition tolerance — способность системы переживать отказы сети (забегая вперед, скажу, что это достаточно вольная, но удобная и в целом правильная трактовка).
Теорема постулирует, что при дизайне распределенной системы вы не сможете создать такую систему, которая будет одновременно удовлетворять всем трем этим свойствам: быть и согласованной, и доступной, и при этом устойчивой к разрывам сети. Вы можете реализовать системы, которые будут удовлетворять не более чем двум любым из этих трех свойств. Здесь вроде бы все очевидно и понятно. Теперь мы можем синхронизироваться с автором статьи в понимании трех упомянутых выше свойств.
Итак, под консистентностью (Consistency) понимается свойство линеаризуемости (атомарности). Это (практически) самая сильная гарантия согласованности, которая, если говорить простыми словами, заключается в следующем: любое чтение должно увидеть последнюю зафиксированную в системе запись. Это значит, например, что любая база данных с
* асинхронной репликацией, если возможно чтения данных с фолловеров
* синхронной репликацией, если возможно чтение с фолловеров без кворума
не линеаризуема, т.е. она по определению не удовлетворяет свойству C. Огромное количество систем, подпадающих под описание выше, которые все же дают такой достаточно сильный уровень согласованности, как sequential consistency (при котором пользователь все же может прочитать устаревшие, но актуальные в прошлом данные) не являются консистентными. Вполне возможно, что в вашем случае подойдут еще более слабые гарантии согласованности.
Под доступностью (Availability) имеется в виду такое свойство, что если у вас есть кластер из нескольких распределенных, но соединенных сетью узлов, то любой узел в этой системе, если он находится в рабочем состоянии, должен иметь возможность ответить на любой ваш запрос (на чтение или запись) за конечный промежуток времени.
Звучит как очень сильная гарантия, но на самом деле, в большинстве распределенных систем этой гарантии будет недостаточно. В большинстве распределенных систем под доступностью будет, конечно же, пониматься соблюдение SLA на время, в течение которого пользователь получит ответ. Очевидно, что если ваша система/узел ответит на запрос за десять минут, то любой из пользователей сочтет вашу систему недоступной, хотя формально она будет удовлетворять требованиям CAP-теоремы.
Когда речь заходит о NoSQL, то чаще всего вспоминают о CAP-теореме, так как она описывает, какими свойствами обладают распределенные системы. Мы можем быстро окрестить ту или иную технологию CA, AP, CP, тем самым внести ясность для всех участников дискуссии, например, при выборе очередной БД для проекта 🧐 На самом деле сфера применения CAP-теоремы гораздо уже, чем это может показаться на первый взгляд. Предлагаю разобраться, в чем собственно суть теоремы, что значит каждая из букв в этой аббревиатуре, когда её можно применять, а когда — нет.
Несколько удивительно, что трактовку CAP-теоремы помнят абсолютно все, а вот значения понятий, которыми она оперирует, достаточно сильно искажают (хотя в английской статье на Википедии трактовка дана вполне корректная. А ведь достаточно просто обратиться к первоисточнику, что мы с вами и сделаем.
В соответствии с документом в контексте распределенных систем рассматриваются три свойства:
* Consistency — консистентность данных в системе;
* Availability — доступность узлов системы;
* Partition tolerance — способность системы переживать отказы сети (забегая вперед, скажу, что это достаточно вольная, но удобная и в целом правильная трактовка).
Теорема постулирует, что при дизайне распределенной системы вы не сможете создать такую систему, которая будет одновременно удовлетворять всем трем этим свойствам: быть и согласованной, и доступной, и при этом устойчивой к разрывам сети. Вы можете реализовать системы, которые будут удовлетворять не более чем двум любым из этих трех свойств. Здесь вроде бы все очевидно и понятно. Теперь мы можем синхронизироваться с автором статьи в понимании трех упомянутых выше свойств.
Итак, под консистентностью (Consistency) понимается свойство линеаризуемости (атомарности). Это (практически) самая сильная гарантия согласованности, которая, если говорить простыми словами, заключается в следующем: любое чтение должно увидеть последнюю зафиксированную в системе запись. Это значит, например, что любая база данных с
* асинхронной репликацией, если возможно чтения данных с фолловеров
* синхронной репликацией, если возможно чтение с фолловеров без кворума
не линеаризуема, т.е. она по определению не удовлетворяет свойству C. Огромное количество систем, подпадающих под описание выше, которые все же дают такой достаточно сильный уровень согласованности, как sequential consistency (при котором пользователь все же может прочитать устаревшие, но актуальные в прошлом данные) не являются консистентными. Вполне возможно, что в вашем случае подойдут еще более слабые гарантии согласованности.
Под доступностью (Availability) имеется в виду такое свойство, что если у вас есть кластер из нескольких распределенных, но соединенных сетью узлов, то любой узел в этой системе, если он находится в рабочем состоянии, должен иметь возможность ответить на любой ваш запрос (на чтение или запись) за конечный промежуток времени.
Звучит как очень сильная гарантия, но на самом деле, в большинстве распределенных систем этой гарантии будет недостаточно. В большинстве распределенных систем под доступностью будет, конечно же, пониматься соблюдение SLA на время, в течение которого пользователь получит ответ. Очевидно, что если ваша система/узел ответит на запрос за десять минут, то любой из пользователей сочтет вашу систему недоступной, хотя формально она будет удовлетворять требованиям CAP-теоремы.
🤔4
Вполне возможно, что вам надо дать гарантию доступности гораздо сильнее, чем в CAP-теореме. Например, у вас есть два дата-центра, и между ними произошел разрыв сети. Каждый из дата-центров, как и ноды в каждом из них — живы. По CAP-теореме для доступности вы обязаны давать возможность пользоваться всеми этими нодами. Но если же ваша система при определении такой ситуации начинает перенаправлять все запросы пользователя в один единственный дата-центр, то на самом деле ваша доступность как системы может быть вообще стопроцентной, то есть она может быть абсолютно доступной, но с точки зрения CAP-теоремы она доступной являться не будет, потому что часть нод, которые живы, тем не менее, не могут обслужить пользовательские запросы.
Перейдем к последнему свойству — это Partition tolerance. Исходно это возможность возникновения разрывов в сети, при которых сообщения между узлами системы могут теряться. Часто свойство перефразируют до “способность системы переживать отказы сети”, что в целом не сильно влияет на ход рассуждений. Что понимается под отказом сети? Это случай, когда вы не получаете никаких сообщений от узла А в узел Б и от узла Б в узел А, т.е. все сетевые пакеты потеряны. Вообще говоря, любая распределенная система должна переживать такие ситуации, даже несмотря на то, что все сообщения теряются между этими узлами.
Сразу же хочется вспомнить о CA-системах. Они консистентны и доступны, но тогда они не распределенные, потому что в любой распределенной сети между узлами есть сеть, а сеть в любой момент может отказать (здесь как раз лучше работает исходное определение автора). К этой группе можно отнести РСУБД, развернутые на одном узле.
Вы должны помнить, что в контексте CAP-теоремы, CP-система, например, не должна допускать чтения устаревших данных. Если вы говорите, что ваша система является AP, то вы на самом деле даете не такую уж сильную гарантию, как возможно хотите дать, потому что в распределенных системах вам часто нужна гораздо более сильная гарантия доступности, учитывающая в том числе SLA на время ответа системы.
Как вы уже, наверное, поняли, обращаясь к CAP-теореме, вы должны быть очень аккуратны. Теорема очерчивает строгие границы применимости. Еще раз, CAP-теорема — хороший инструмент, если вы им правильно пользуетесь. В остальном вы должны просто рассуждать о компромиссе между консистентностью и доступностью вашей системы (но, вероятно, уже не в терминах CAP-теоремы).
Если вам хочется почитать рефлексию на тему, то рекомендую обратиться к статье классика, Мартина Клепмана (автор книги с кабанчиком), где он подробно прошелся по каждой из характеристик.
Перейдем к последнему свойству — это Partition tolerance. Исходно это возможность возникновения разрывов в сети, при которых сообщения между узлами системы могут теряться. Часто свойство перефразируют до “способность системы переживать отказы сети”, что в целом не сильно влияет на ход рассуждений. Что понимается под отказом сети? Это случай, когда вы не получаете никаких сообщений от узла А в узел Б и от узла Б в узел А, т.е. все сетевые пакеты потеряны. Вообще говоря, любая распределенная система должна переживать такие ситуации, даже несмотря на то, что все сообщения теряются между этими узлами.
Сразу же хочется вспомнить о CA-системах. Они консистентны и доступны, но тогда они не распределенные, потому что в любой распределенной сети между узлами есть сеть, а сеть в любой момент может отказать (здесь как раз лучше работает исходное определение автора). К этой группе можно отнести РСУБД, развернутые на одном узле.
Вы должны помнить, что в контексте CAP-теоремы, CP-система, например, не должна допускать чтения устаревших данных. Если вы говорите, что ваша система является AP, то вы на самом деле даете не такую уж сильную гарантию, как возможно хотите дать, потому что в распределенных системах вам часто нужна гораздо более сильная гарантия доступности, учитывающая в том числе SLA на время ответа системы.
Как вы уже, наверное, поняли, обращаясь к CAP-теореме, вы должны быть очень аккуратны. Теорема очерчивает строгие границы применимости. Еще раз, CAP-теорема — хороший инструмент, если вы им правильно пользуетесь. В остальном вы должны просто рассуждать о компромиссе между консистентностью и доступностью вашей системы (но, вероятно, уже не в терминах CAP-теоремы).
Если вам хочется почитать рефлексию на тему, то рекомендую обратиться к статье классика, Мартина Клепмана (автор книги с кабанчиком), где он подробно прошелся по каждой из характеристик.
👍4
To cache or not to cache, in Redis lies the power
На днях выбирали технологии, которые будем завозить в новый проект. Размещать инфраструктуру планируем в Yandex.Cloud. Нагрузка на систему по моим скромным подсчетам вряд ли когда-то вырастет даже до 100 rps. В связи с этим возник вопрос: нужно ли нам рассмотреть возможность затащить Redis в проект?
Когда мы слышим о Redis, то сразу вспоминаем о кэше. Даже акроним Remote Dictionary Server намекает нам о простоте технологии. Однако стоит помнить, что Redis — это нечто большее, чем просто кэш. Давайте рассмотрим другие сферы его применения.
Первое и самое простое применение — key-value хранилище. В качестве value могут быть как достаточно простые объекты, такие как сессии пользователя, так и витиеватые документы, которые мы храним обычно в MongoDB (если вам нужны нативная поддержка вторичных индексов, гибкий язык запросов и гарантия сохранности данных). Вообще Redis часто используется в сочетании с MongoDB, которую выбирают основным решением для хранения данных.
Не в последнюю очередь Redis любят за то, что он предоставляет возможность работать с разными внутренними структурами данных, такими как:
- связные списки (Lists), с помощью которых можно организовать блокирующую очередь, например, для коммуницирующих между собой процессов (паттерн consumer-producer), или запоминать последние N элементов
- множества (Sets) с рядом соответствующих удобных команд
- отсортированные множества (Sorted sets), которые позволяют реализовать алгоритм sliding-window для ограничения трафика или банальный топ чего-либо
- потоки (Streams) с (шок) consumer-группами, которые можно использовать для фиксации и последующей обработки потока событий
- битовые карты (Bitmaps), которые можно использовать для реализации фильтра Блума или экономного отслеживания пользователей онлайн
- битовые поля (Bitfields), которые являются более гибкой альтернативой простым битовым картам
- геоиндексы (Geospatial), которые удобно использовать для работы с координатами
Особенно привлекательным выглядит наличие атомарных операций в некоторых структурах.
Сами по себе эти встроенные структуры данных Redis уже предоставляют широкий спектр возможностей, однако дополнительные библиотеки, используя эти мощные строительные блоки, выводят разработку на новый уровень продуктивности. Только посмотрите, как много возможностей предоставляет, например, библиотека radisson, написанная на Java. Мы в свой проект затащили распределенную (кластерную) блокировку RedLock, которую используем, например, для fairless доступа к распределенному ресурсу, который не может из-за нехватки ресурсов обрабатывать более одного запроса единовременно.
Подведем итоги? Говорить о Redis только в контексте кэширования невозможно. Это in-memory хранилище благодаря своей гибкости можно удобно применить к решению самых разных бизнес-задач. Мы точно затащим Redis к себе в проект и, надеюсь, сможем применить его не только как банальный кэш.
На днях выбирали технологии, которые будем завозить в новый проект. Размещать инфраструктуру планируем в Yandex.Cloud. Нагрузка на систему по моим скромным подсчетам вряд ли когда-то вырастет даже до 100 rps. В связи с этим возник вопрос: нужно ли нам рассмотреть возможность затащить Redis в проект?
Когда мы слышим о Redis, то сразу вспоминаем о кэше. Даже акроним Remote Dictionary Server намекает нам о простоте технологии. Однако стоит помнить, что Redis — это нечто большее, чем просто кэш. Давайте рассмотрим другие сферы его применения.
Первое и самое простое применение — key-value хранилище. В качестве value могут быть как достаточно простые объекты, такие как сессии пользователя, так и витиеватые документы, которые мы храним обычно в MongoDB (если вам нужны нативная поддержка вторичных индексов, гибкий язык запросов и гарантия сохранности данных). Вообще Redis часто используется в сочетании с MongoDB, которую выбирают основным решением для хранения данных.
Не в последнюю очередь Redis любят за то, что он предоставляет возможность работать с разными внутренними структурами данных, такими как:
- связные списки (Lists), с помощью которых можно организовать блокирующую очередь, например, для коммуницирующих между собой процессов (паттерн consumer-producer), или запоминать последние N элементов
- множества (Sets) с рядом соответствующих удобных команд
- отсортированные множества (Sorted sets), которые позволяют реализовать алгоритм sliding-window для ограничения трафика или банальный топ чего-либо
- потоки (Streams) с (шок) consumer-группами, которые можно использовать для фиксации и последующей обработки потока событий
- битовые карты (Bitmaps), которые можно использовать для реализации фильтра Блума или экономного отслеживания пользователей онлайн
- битовые поля (Bitfields), которые являются более гибкой альтернативой простым битовым картам
- геоиндексы (Geospatial), которые удобно использовать для работы с координатами
Особенно привлекательным выглядит наличие атомарных операций в некоторых структурах.
Сами по себе эти встроенные структуры данных Redis уже предоставляют широкий спектр возможностей, однако дополнительные библиотеки, используя эти мощные строительные блоки, выводят разработку на новый уровень продуктивности. Только посмотрите, как много возможностей предоставляет, например, библиотека radisson, написанная на Java. Мы в свой проект затащили распределенную (кластерную) блокировку RedLock, которую используем, например, для fairless доступа к распределенному ресурсу, который не может из-за нехватки ресурсов обрабатывать более одного запроса единовременно.
Подведем итоги? Говорить о Redis только в контексте кэширования невозможно. Это in-memory хранилище благодаря своей гибкости можно удобно применить к решению самых разных бизнес-задач. Мы точно затащим Redis к себе в проект и, надеюсь, сможем применить его не только как банальный кэш.
👍9
Слоистая архитектура, часть 1
Если взять любой мой микросервис (МС), то он будет построен в соответствии с многослойной (слоистой/многоуровневой) архитектурой. Это классические для web-приложения на Spring слои: Controller, Service, Repository. Большинство разработчиков на Spring научились делить приложение на слои, обращаться только к нижележащим слоям (но не всегда строго к одному нижележащему) и узлам того же слоя без создания циклических зависимостей. В борьбе с циклическими зависимостями помогает сам фреймворк, выбрасывая логичное исключение: BeanCurrentlyInCreationException. Однако некоторые интервьюеры все же захотят узнать у вас, как обойти это ограничение (конечно, без помощи рефакторинга :). Такие извращенные способы можно найти, например, здесь.
Для того чтобы показать слои приложения наглядно, я пошел в интернеты. Картинка ниже (первая) показалась мне достаточно занятной. Она взята из статьи, которая рассказывает, в том числе о веб-фреймворках, но предлагаю переложить ее на рассматриваемые MVC-приложения. Для этого попрошу вас:
- узел, на который смотрит конец каждой стрелки, рассматривать как пользователя некоторой функциональности узла из нижележащего слоя
- не обращать внимания на подписи Component
- рассматривать Controller как API-слой сервиса, который предоставляет доступ к некоторому домену, а View — как слой, отвечающий за формирование ответов для отображения экранов на фронтах (смесью домена и UI)
- мысленно объединить все Repository в один слой, Service — в другой и т.д.
Если картинка настолько неточна, то как она вообще попала в статью? Мы в нашем подразделении разрабатываем преимущественно BFF (Backend For Front-end), поэтому мне хочется остановиться на объединении пунктиром слоев View и Controller, так как до недавнего времени эти слои у нас в сервисах были сцеплены воедино (в слой Controller). Каким образом это выражалось? В МСе могли быть перемешаны эндпоинты, которые отвечают за UI и за работу с доменом. Или хуже, когда один и тот же эндпоинт в системе использовался для обеих этих целей, что зачастую приводило к невозможности повторного их переиспользования. Объединение слоев View и Controller приводило к тому, что каша получилась не только на уровне этого объединенного слоя, но и на уровне Service, который стал по факту объединением уже известного нам Service и некоторой примеси Service View, которая представляет собой UI надстройку над доменом.
Если взять любой мой микросервис (МС), то он будет построен в соответствии с многослойной (слоистой/многоуровневой) архитектурой. Это классические для web-приложения на Spring слои: Controller, Service, Repository. Большинство разработчиков на Spring научились делить приложение на слои, обращаться только к нижележащим слоям (но не всегда строго к одному нижележащему) и узлам того же слоя без создания циклических зависимостей. В борьбе с циклическими зависимостями помогает сам фреймворк, выбрасывая логичное исключение: BeanCurrentlyInCreationException. Однако некоторые интервьюеры все же захотят узнать у вас, как обойти это ограничение (конечно, без помощи рефакторинга :). Такие извращенные способы можно найти, например, здесь.
Для того чтобы показать слои приложения наглядно, я пошел в интернеты. Картинка ниже (первая) показалась мне достаточно занятной. Она взята из статьи, которая рассказывает, в том числе о веб-фреймворках, но предлагаю переложить ее на рассматриваемые MVC-приложения. Для этого попрошу вас:
- узел, на который смотрит конец каждой стрелки, рассматривать как пользователя некоторой функциональности узла из нижележащего слоя
- не обращать внимания на подписи Component
- рассматривать Controller как API-слой сервиса, который предоставляет доступ к некоторому домену, а View — как слой, отвечающий за формирование ответов для отображения экранов на фронтах (смесью домена и UI)
- мысленно объединить все Repository в один слой, Service — в другой и т.д.
Если картинка настолько неточна, то как она вообще попала в статью? Мы в нашем подразделении разрабатываем преимущественно BFF (Backend For Front-end), поэтому мне хочется остановиться на объединении пунктиром слоев View и Controller, так как до недавнего времени эти слои у нас в сервисах были сцеплены воедино (в слой Controller). Каким образом это выражалось? В МСе могли быть перемешаны эндпоинты, которые отвечают за UI и за работу с доменом. Или хуже, когда один и тот же эндпоинт в системе использовался для обеих этих целей, что зачастую приводило к невозможности повторного их переиспользования. Объединение слоев View и Controller приводило к тому, что каша получилась не только на уровне этого объединенного слоя, но и на уровне Service, который стал по факту объединением уже известного нам Service и некоторой примеси Service View, которая представляет собой UI надстройку над доменом.
Слоистая архитектура, часть 2
Каждый МС берет на себя как минимум две ответственности. К чему это приводит? МСы, которые могли отвечать только за UI или только за домен — смешиваются в один слой. Конечно, аналитику или разработчику разобраться, какие МСы и как между собой взаимодействуют, становится сложнее. Это является причиной возникновения циклических зависимостей уже на уровне взаимодействия МСов (когда систему нельзя представить в виде направленного ациклического графа). Такую систему очень тяжело поддерживать и масштабировать.
Получилось так, что архитектура МСа повлияла на архитектуру системы. Исходно в системе имела место ошибка проектирования, при которой произошло перемешивание слоев на всех уровнях. Требовалось выработать новый подход, чтобы наш BFF был поддерживаемым. К какой модели мы пришли (картинка ниже, вторая)?
view-api — это API, отвечающая за агрегацию доменных данных и вывод необходимой информации на экран клиенту. Требования к view-api предъявляются следующие:
- все эндпоинты view-api доступны только с фронтов, т.е. вызывать их из других API нельзя
- данные для отображения на экране, которые не приходят из core-api, должны прописываться в конфигурации view-api (текст, цвета кнопок, фона и др.)
- из view-api нельзя обращаться напрямую в back-системы
core-api — API, отвечающая за получение и обработку доменных данных с возможностью переиспользования. Требования к core-api предъявляются следующие:
- все ручки core-api недоступны с фронтов
- core-api ничего не знают про данные для отображения на экране (текст, цвета кнопок и фона), только если не проксируют эти данные с back-системы
- в идеале обращений между core-api тоже быть не должно
По результатам анализа системы мы пришли к строгой (обращение между узлами одного слоя или строго одного нижележащего слоя) многослойной архитектуре на уровне взаимодействия сервисов в системе. Теперь каждый сервис отвечает либо за работу с доменом, либо за UI. Мы развязали эти независимые модули/слои, которые ранее были скреплены, чтобы добиться лучшего повторного использования (особенно доменных сервисов). За счет чего достигается строгость? Фронты теперь могут обращаться только к view-api, view-api — только к core-api, а core-api — только к back-системам и друг к другу.
Каждый МС берет на себя как минимум две ответственности. К чему это приводит? МСы, которые могли отвечать только за UI или только за домен — смешиваются в один слой. Конечно, аналитику или разработчику разобраться, какие МСы и как между собой взаимодействуют, становится сложнее. Это является причиной возникновения циклических зависимостей уже на уровне взаимодействия МСов (когда систему нельзя представить в виде направленного ациклического графа). Такую систему очень тяжело поддерживать и масштабировать.
Получилось так, что архитектура МСа повлияла на архитектуру системы. Исходно в системе имела место ошибка проектирования, при которой произошло перемешивание слоев на всех уровнях. Требовалось выработать новый подход, чтобы наш BFF был поддерживаемым. К какой модели мы пришли (картинка ниже, вторая)?
view-api — это API, отвечающая за агрегацию доменных данных и вывод необходимой информации на экран клиенту. Требования к view-api предъявляются следующие:
- все эндпоинты view-api доступны только с фронтов, т.е. вызывать их из других API нельзя
- данные для отображения на экране, которые не приходят из core-api, должны прописываться в конфигурации view-api (текст, цвета кнопок, фона и др.)
- из view-api нельзя обращаться напрямую в back-системы
core-api — API, отвечающая за получение и обработку доменных данных с возможностью переиспользования. Требования к core-api предъявляются следующие:
- все ручки core-api недоступны с фронтов
- core-api ничего не знают про данные для отображения на экране (текст, цвета кнопок и фона), только если не проксируют эти данные с back-системы
- в идеале обращений между core-api тоже быть не должно
По результатам анализа системы мы пришли к строгой (обращение между узлами одного слоя или строго одного нижележащего слоя) многослойной архитектуре на уровне взаимодействия сервисов в системе. Теперь каждый сервис отвечает либо за работу с доменом, либо за UI. Мы развязали эти независимые модули/слои, которые ранее были скреплены, чтобы добиться лучшего повторного использования (особенно доменных сервисов). За счет чего достигается строгость? Фронты теперь могут обращаться только к view-api, view-api — только к core-api, а core-api — только к back-системам и друг к другу.
Кто готов посмотреть битву 20$ / month ChatGPT 4 против Completely Free Llama-3?
Попробуем отрефакторить один и тот же метод на языке Kotlin с помощью двух этих моделей здесь.
Попробуем отрефакторить один и тот же метод на языке Kotlin с помощью двух этих моделей здесь.
👨💻4👍2
Рефакторинг. Выделение метода, часть 1
Кажется, все знают, что функции/методы нужно делать короче. Вот как восторгается Роберт Мартин программе, написанной с помощью лаконичных функций:
… каждая функция в программе … занимала всего две, три или четыре строки. Все функции были предельно очевидными. Каждая функция излагала свою историю, и каждая история естественным образом подводила вас к началу следующей истории. Вот какими короткими должны быть функции!
Давайте разберем этот абзац, чтобы понять, как написать программу, которая порадовала бы Роберта Мартина. В первую очередь — это следовать SRP, однако не нужно ударяться в крайности, начиная делить код на множество микроскопических функций. Важно, чтобы те операции, которые вы выносите в функцию, находились на одном и том же уровне абстракции и являлись семантически независимыми (по отношению к остальному коду) и значимыми. Именно в таком случае функции будут представлять из себя строительные блоки, из которых может быть построена поддерживаемая программа. А дальше все просто: будучи носителями одной ответственности/семантики таким функциям несложно будет дать ясное и точное имя.
Кажется, все знают, что функции/методы нужно делать короче. Вот как восторгается Роберт Мартин программе, написанной с помощью лаконичных функций:
… каждая функция в программе … занимала всего две, три или четыре строки. Все функции были предельно очевидными. Каждая функция излагала свою историю, и каждая история естественным образом подводила вас к началу следующей истории. Вот какими короткими должны быть функции!
Давайте разберем этот абзац, чтобы понять, как написать программу, которая порадовала бы Роберта Мартина. В первую очередь — это следовать SRP, однако не нужно ударяться в крайности, начиная делить код на множество микроскопических функций. Важно, чтобы те операции, которые вы выносите в функцию, находились на одном и том же уровне абстракции и являлись семантически независимыми (по отношению к остальному коду) и значимыми. Именно в таком случае функции будут представлять из себя строительные блоки, из которых может быть построена поддерживаемая программа. А дальше все просто: будучи носителями одной ответственности/семантики таким функциям несложно будет дать ясное и точное имя.
👍3
Рефакторинг. Выделение метода, часть 2
Следование SRP выглядит несложно, однако всегда ли мы можем добиться его выполнения? Если вычисления в большом методе идут последовательно, где результаты каждого предыдущего этапа передаются следующему, как по конвейеру, то проблем не возникает. Но что, если это автономные потоки вычислений, которые многократно пронизывают друг друга в процессе прохождения по функции? На ум сразу приходят проекты, в которых используются библиотеки с низким уровнем абстракции. В данном случае, например, пользовательский поток переплетается с техническим, необходимым для управления работой клиента.
Что еще можно привести в качестве примеров? Конечно, использование сквозной функциональности:
- поэтапные логирование и аудит;
- замеры производительности отдельных участков кода и сбор метрик;
- и т.д.
Вот, например, поток аудита переплетается с бизнес-функциональностью подтверждения оффера:
Этот код можно было бы также оформить в виде конечного автомата, однако такое решение кажется здесь избыточным из-за малого числа состояний. Еще одним вариантом разделения двух несвязанных семантик в данном примере может быть применение техник АОП, которые однако не славятся простотой чтения и поддержки.
Что если в препарируемом методе нет автономных потоков вычислений, но и выделение новых методов кажется невозможным? Вероятно, в таком случае мы имеем дело с плохо написанным, запутанным кодом, который требует предварительной подготовки с использованием других техник рефакторинга.
Следование SRP выглядит несложно, однако всегда ли мы можем добиться его выполнения? Если вычисления в большом методе идут последовательно, где результаты каждого предыдущего этапа передаются следующему, как по конвейеру, то проблем не возникает. Но что, если это автономные потоки вычислений, которые многократно пронизывают друг друга в процессе прохождения по функции? На ум сразу приходят проекты, в которых используются библиотеки с низким уровнем абстракции. В данном случае, например, пользовательский поток переплетается с техническим, необходимым для управления работой клиента.
Что еще можно привести в качестве примеров? Конечно, использование сквозной функциональности:
- поэтапные логирование и аудит;
- замеры производительности отдельных участков кода и сбор метрик;
- и т.д.
Вот, например, поток аудита переплетается с бизнес-функциональностью подтверждения оффера:
suspend fun confirmOffer(...) {
...
try {
auditService.updateConfirmationStatus(auditReference, OperationStatus.PROGRESS)
offerService.confirmOffer(...)
auditService.updateConfirmationStatus(auditReference, OperationStatus.SUCCESS)
...
} catch (e: Exception) {
...
auditService.updateConfirmationStatus(auditReference, OperationStatus.FAIL)
...
}Этот код можно было бы также оформить в виде конечного автомата, однако такое решение кажется здесь избыточным из-за малого числа состояний. Еще одним вариантом разделения двух несвязанных семантик в данном примере может быть применение техник АОП, которые однако не славятся простотой чтения и поддержки.
Что если в препарируемом методе нет автономных потоков вычислений, но и выделение новых методов кажется невозможным? Вероятно, в таком случае мы имеем дело с плохо написанным, запутанным кодом, который требует предварительной подготовки с использованием других техник рефакторинга.
👍2
Предлагаю на примере Apache Kafka рассмотреть, какие есть подводные камни при сборе и использовании метрик. Не пугайтесь, если вы не знакомы с Kafka — эти идеи могут всплыть при мониторинге абсолютно любой системы.
👍2