Организованное программирование | Кирилл Мокевнин
10.3K subscribers
55 photos
208 links
Как из джуниора дойти до мидла, а потом и до синьора
Ютуб https://youtube.com/@mokevnin
Связь для предложений: @kirillpublic
Download Telegram
Фиганул на хабр статью (мне кажется оч классную) на тему связи архитектуры и ооп. В ней многое перекликается с тем что я говорил в выпуске подлодки про чистый код. Плюсы к статье приветствуются https://habr.com/ru/articles/833968/
Саасы и печаль
Для своих задач мы используем десятки разнообразных сервисов для кодинга, маркетинга, продаж, аналитики, сапорта и всего на свете. Суммарно около 80 сервисов, большая часть из которых платная. И со всеми этими сервисами есть несколько проблем, которые заставили меня поменять отношение к выбору таких сервисов. Вот они:

⁃ То что дешево в начале, может стать очень дорогим в процессе. Пример zapier, стоимость этого сервиса может расти экспоненциально. Тоже самое со всякими crm типа mailchimp
⁃ Раз в год какой-нибудь сервис так меняет условия или цены, что пользоваться им становится накладно. Приходится искать альтернативы
⁃ Санкции, законодательство и другие форс-мажоры из-за которых приходится постоянно мигрировать туда сюда.

В общем беда, любой сервис так или иначе входит в состояние, когда надо двигать дальше. Кто-то скажет, ребят, ставьте все сразу себе, но для небольших продуктовых компаний это слишком дорого и не нужно по большому счету. Поэтому мы пошли по третьему пути. Сейчас мы стараемся выбирать продукты, которые предоставляют saas, но с возможностью съехать в self-hosted решение. Эта тактика уже не раз себя оправдывала, потому что часть сервисов реально заставила нас съехать на свои сервера. Примеры таких сервисов:

⁃ gitlab, сначала юзали облако, после изменения политики оплаты (за юзеров) съехали к себе
⁃ n8n (коннектор между сервисами), когда событий стало много, поехали к себе. По пути прошли zapier и pipedream
⁃ posthog (продуктовая аналитика), пока на облаке, но в любой момент готовимся переехать. Были на амплитуде.
⁃ mattermost, в какой-то момент начали банить тех кто из россии, поэтому уехали на свои сервера. Съехали со слака.
⁃ airbyte (ELT pipline), переехали на него с какого-то другого сервиса
sentry.io, пока в облаке, но всегда можем уехать на self-hosted

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

Ссылки: Телеграм | Youtube | Подкаст
Управление конфигурацией

Для настройки окружения проекта можно использовать (а многие так и делают) стандартные средства операционной системы. Такие, как пакетный менеджер (yum, apt), прямое редактирование конфигурационных файлов, bash-скрипты, curl/wget и многое другое.

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

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

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

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

Может показаться, что решением является написание, например, bash-скриптов. И в любом случае это уже шаг вперед. Но есть одна серьезная проблема. Это обеспечение идемпотентности. Идемпотентная операция в информатике — действие, многократное повторение которого эквивалентно однократному. Что на практие означает отсутствие идемпотентности? То, что повторный запуск bash-скрипта (кроме тривиальных случаев) приведет к ошибке и остановке выполнения.

Примеры:

Вызов mkdir упадет с ошибкой что "директория существует". Операции перенаправления вывода повторно запишут данные. Любая операция, связанная с удалением, закончится с ошибкой (mv, rm, rmdir). Клонирование git репозитория упадет с ошибкой. ln упадет с ошибкой. И многое другое. Это означает, что вам нужно либо всегда накатывать bash-скрипт на чистую систему, что невозможно, либо вам нужно будет все скрипты обвешивать проверками, которые и будут обеспечивать идемпотентность.

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

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

К счастью, решение существует, и это не ручные скрипты, а системы управления конфигурацией. Их достаточно много и все они предлагают концепцию «управление инфраструктурой как программным кодом». Это означает, что описание инфраструктуры хранится в файлах (плейбуках, кукбуках и так далее), находящихся под контролем версий, а сама инфраструктура изменяется только посредством запуска процесса накатки изменений. Также эти системы знают про топологию серверов и позволяют вам гибко указывать, что к чему относится, дают возможности легко переиспользовать повторяющиеся сценарии, а так же выполнять множество других полезных функций. Лично я рекомендую Ansible, я даже когда-то делал курс https://ru.hexlet.io/courses/ansible

Ссылки: Телеграм | Youtube | Подкаст
Организованное программирование | Кирилл Мокевнин pinned «Все же слышали или пользовались Миро? У ребят кодовая база на миллионы строк и спец команда frontops, которая занимается только инструментарием и производительностью. Обо всем этом поболтали с Тимуром Хазамовым, разработчиком core https://www.youtube.com/…»
Что такое рыночная зарплата на самом деле? Это не представления людей о том сколько они стоят, это не зарплаты которые вы видите в вакансиях, это не медианные значения которые выводят в аналитических статьях. Рычная зарплата, это та зарплата на которую вы (бизнес не бизнес, не важно) можете найти нужного специалиста на рынке. Поэтому понятия "зарплата ниже рыночной" это миф. Обычно, речь идет про то, что для вас это недостаточно (по вашему опыту или вашему представлению), но это не значит, что таких людей нет. И даже если предположить что в вакансии зарплата действительно ниже рыночной, то благодаря свободному рынку, она достаточно быстро корректируется иначе вакансия просто не закроется. А причина почему бизнес пытается не переплатить очень простая, но я ее расскажу в будущих постах если вам интересно.

- Поставьте палец вверх если интересно и я напишу
- Поставьте палец вниз, если вы не хотите про это читать

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


Ссылки: Телеграм | Youtube | Подкаст
Васятка делает стартап. Часть первая

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

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

Вася повезло: он встретился со своими друзьями-предпринимателями, которые объяснили, что бизнес это не случайное стечение обстоятельств, а план, который надо составить. Этот план называется финмоделью и учитывает будущие расходы и доходы. Как и полагается опытному разработчику, Вася считал, что хороший продукт продает себя сам, поэтому почти не предусмотрел средств на привлечение клиентов. Он прикинул, что за полгода наберет через сарафанное радио 5 тысяч подписчиков по $10 в месяц, что позволит покрыть расходы на команду, офис и налоги, а также получать прибыль. План был готов, пора приступать, решил подающий надежды финансист.

Вася нанял двух разработчиков, арендовал офис, и за пару месяцев они создали первый прототип. Затем выложил приложение в App Store и рассказал об этом своим друзьям в соц сетях. Первые 50 подписчиков пришли за неделю, что вселило в нашего бизнесмена уверенность. Но во вторую неделю добавилось лишь 10 новых подписчиков, а через неделю отписались 5 старых. В App Store появились негативные отзывы, потому что продукт был сырой. Но Вася не унывал — с командой быстро исправил баги и добавил новые функции. Через пару недель обновленное приложение появилось в App Store, но клиенты перестали приходить.

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

Наш герой осознал, что ему придется заниматься рекламой. Он потратил неделю на настройку и запуск, а затем, вложив в рекламу тысячу долларов, с ужасом обнаружил, что привлек всего 10 клиентов. То есть каждый клиент обошелся ему в $100, при том что подписка приносит только $10 в первый месяц. "При таком раскладе я всегда буду в минусе," — подумал Вася. Он снова пошел к друзьям, которые уже спорили, как долго он продержится до закрытия.

Друзья объяснили ему понятия стоимости привлечения клиента (CAC) и доходов за время его использования продукта (LTV). Вася слышал о юнит-экономике, но только теперь начал понимать её суть. Чтобы зарабатывать, CAC должен быть в несколько раз ниже LTV, иначе бесмысленно.

"Как это возможно, если у меня CAC уже $100?" — спросил Вася. Ему ответили: "Твоя задача — снизить CAC и увеличить LTV, пока юнит-экономика не сойдется. Чтобы увеличить LTV, делай продукт лучше и полезнее для клиента. Чтобы снизить CAC, ищи дешевые каналы привлечения и повышай узнаваемость продукта, пиши статьи и снимай видео. Но это может занять годы без внешних инвестиций"

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

Чо как вам? 😄

Ссылки: Телеграм | Youtube | VK
Пост, в котором, для удобства, собраны ссылки на каналы, через которые можно меня читать, смотреть и слушать. Поехали:

Ютуб: Выпуски + шортсы
- Facebook: Анонсы
Сообщество вконтакта (зеркало телеги): Плюс там же подкаст и vk видео где дублируется все с ютуба.
Инстаграм: Нарезки контента, но планируются авторские рилсы
Тикток: Тоже самое что и инста, но для тех кто любит тикток
Рутуб: дубль ютуба
Одноклассники: а что, а вдруг. Копия вконтакта
Твиттер: тут свой контент в основном
Подкасты: Тут все площадки, где можно слушать в аудио (спотифай, apple, яндекс музыка и т.п.)

Если будут появляться какие-то новые штуки, я буду сюда их добавлять. Времена щас такие, что в любой момент могут отрубить там где не ждешь.
Организованное программирование | Кирилл Мокевнин pinned «Пост, в котором, для удобства, собраны ссылки на каналы, через которые можно меня читать, смотреть и слушать. Поехали: ⁃ Ютуб: Выпуски + шортсы - Facebook: Анонсы ⁃ Сообщество вконтакта (зеркало телеги): Плюс там же подкаст и vk видео где дублируется все…»
Зачем нужно знать об идемпотентности

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

Возьмем команду mkdir в линуксе. Она создает директорию: mkdir jopa. Что будет если выполнить эту команду два раза? Во второй раз мы получим ошибку. То есть операция не идемпотентная. Тоже самое справедливо и для многих других команд работающих с файловой системой. Что дальше?

А дальше делаем выводы. Если мы напишем какой-то скрипт, выполняющий подготовку файловой системы, например создающий базовую структуру для нового проекта (типа ./prepare.sh /lala), то отсутствие идемпотентности начнет серьезно мешать. Что может пойти не так?

Во время и после отработки: 1. Отключат свет 2. Не хватит прав 3. Мы что-то случайно удалим и захотим все восстановить 4. Кончится место. И так далее, список может быть большим. Во всех этих ситуациях понадобится повторный запуск и тут бах, он начинает падать с file exists. Соответственно придется вообще все удалять и запускать скрипт так, как будто его раньше не запускали. Мягко говоря это неудобно. Но мы можем обеспечить идемпотентность самостоятельно. Для команды mkdir достаточно добавить флаг -p и повторный запуск перестанет падать с ошибкой

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

Докер тоже решает эту задачу. Так, при сборке образа на основе Dockerfile нам не важно что было раньше, то идемпотентность обеспечивается автоматически. Именно поэтому bash команды внутри Dockerfile это не тоже самое что и реальные bash скрипты. А сборка легко кешируется

Теперь про идемпотентность в HTTP. Там она закреплена прямо на уровне спецификации: https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.2… Например GET, по спецификации, является идемпотентным методом, а POST нет. Тут стоит оговориться что, сам по себе, HTTP этого обеспечить не может. Все зависит от разработчиков. И не знание этих особенностей или игнорирование может приводит к довольно серьезным последствиям.

Из доки: For example, if a client sends a PUT request and the underlying connection is closed before any response is received, then the client can establish a new connection and retry the idempotent request.

Идемпотентность позволяет безопасно делать ретраи и кешировать запросы

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

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

Стандартный способ – добавления ключа идемпотентности. То есть при отправке запроса на списание денег, мы указываем специальный параметр, идентификатор транзакции. Обычно это номер заказа. А платежный сервис на его основе обеспечивает идемпотентность. https://stripe.com/docs/api/idempotent_requests

Идемпотентность особенно важна при работе с очередьми, типа rabbitmq. В этой части ретраи неизбежны и возникает скорее обратная проблема, как остановить то что надо остановить. Тут могут помочь автоматы и превращение at-least-once в at-most-once.

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

Ссылки: Телеграм | Youtube | VK

p.s. Является ли метод DELETE в HTTP идемпотентным
Именование в программировании

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

Нотация

Перед тем, как говорить о семантике, давайте посмотрим на синтаксис. Существует несколько популярных нотаций именования:

• Верблюжья нотация (CamelCase): myClass
• Змеиная нотация (snake_case): my_const
• Шашлычная нотация (kebab-case): my-data
• Особняком стоит Венгерская нотация

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

Размер имеет значение

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

Сущность-Действие
Сравните:


bed(); // bad
sleep(); // good


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

Предикаты

Напомню, что предикат — это функция-проверка, она всегда возвращает либо true, либо false.
В большинстве языков предикаты предваряют префиксом is.


isEmpty()
isValid()
isBusy()


Но не все языки следуют этому правилу. В большинстве лиспов, а так же в ruby (который взял это из лиспов) используется знак ? в конце слова:


empty?
valid?
busy?


Если учесть, что в указанных языках вызов функции не требует скобок в конце, то такая форма смотрится особенно естественной.

Вхождение

Не все предикаты можно выразить через is. Например, как задать вопрос, если мы хотим узнать, есть ли в списке чисел нечетное? В таких ситуациях принято использовать слово has:


node.hasChildren()


Количество

Если вам нужна переменная, в которой содержится количество чего-либо, используйте комбинацию: сущность во множественном числе + count.


symbolsCount
peopleCount


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


errors


Такое именование гарантированно вводит в заблуждение. Сущность во множественном числе всегда должна обозначать только коллекцию.

Примеры


// Нормализация данных
normalizeDomainName('hexlet.io');

// Извлечение части данных
getName(user);
getDomainFromEmail('support@hexlet.io');

// Получение массива с ошибками
const errors = validate(user);
if (errors.length > 0) {
// ...
}

// Подсчеты
calculateDiff(first, second)

// Допуск
canSwim(user)
canViewProfile(user)


p.s. Как бы вы назвали переменную, содержащую массив цен разных товаров после применения скидки?

Ссылки: Телеграм | Youtube | VK
Развитие сюжета про Василия
Когда я начал пост, то планировал как можно быстрее объяснить общие принципы работы бизнесов и нырнуть в особенности связанные с расчетом расходной части куда входит ФОТ. В процессе затянуло и стало интересно писать историю, через которую можно показать не только статическое устройство бизнеса, но и стадии его развития, с попутным объяснением ключевых концепций на разных этапах. Одна из вещей которую я точно хочу показать, это разницу между стартапом и сложившимся бизнесом и переход от одной стадии к другой.

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

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

⁃ Финансовое планирование
⁃ Управленческая отчетность (p&l, баланс, кешфлоу, кассовый метод или метод начислений)
⁃ Юнит-экономика (cac, ltv, arpu, churn rate)
⁃ Маркетинг (перфоманс, посевы, ctr, cpa, crm, посадочные, упаковка, seo)
- Аналитика (сквозная аналитика, аналитика продаж, продуктовая аналитика)
⁃ Продажи
⁃ Менеджмент (KPI, дерево метрик)
⁃ Рынки (рост, алый океан, product marketing fit, конкуренты)
⁃ Продукт

Ссылки: Телеграм | Youtube | VK

p.s. Следующую часть планирую написать завтра или на выходных
Что происходит в JS/TS

После выпуска с Андреем пришло много интересных комментов плюс кое что обсудили в твиттере. И вот что интересного оттуда можно вынести:

⁃ Graphql был ошибкой. https://x.com/mokevnin/status/1824431153295577236
⁃ Все массово валят (и довольны) на https://trpc.io/
⁃ Нормальная ORM (почти) в Node.js для ts и js это Drizzle (призма нет) https://orm.drizzle.team/
⁃ Хуки в реакте были ошибкой: https://x.com/mokevnin/status/1824148007820161095 охеренный тред с Ситником
- react-query превратился в tanstack, который умеет все для всех фреймворков https://tanstack.com/
⁃ В js есть интересная экосистема тул https://unjs.io/
- zod классная либа для валидации https://zod.dev/
⁃ Vue > 3.3 офигенен https://x.com/xanf_ua/status/1824130658790240456
- Я умею дышать маткой
Васятка делает стартап. Часть вторая

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

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

Подсчет LTV (life Time Value)

Вася понял, где-то сильно просчитался. Несмотря на то, что он установил цену подписки в $10, далеко не все пользователи оставались на платформе на длительный срок. Отток пользователей (Churn Rate) оказался выше ожидаемого, и за первый месяц составил примерно 30.74%. Это означало, что почти треть всех пользователей отписывались каждый месяц, что било по самолюбию Васи, ведь его продукт был лучше тех, которыми он пользовался сам. В результате, средний пользователь приносил ему лишь $25, прежде чем отписывался.

Что тут не так, подумал Вася и решил спросить у ChatGPT, а есть ли какие-то средние показатели оттока пользователей у таких приложений, на которые он мог бы ориентироваться? ChatGPT сказал, что в среднем отток составляет 5-10% в месяц. При таком оттоке, ltv бы составил 100$-200$, что уже значительно интереснее. Похоже надо было связаться с пользователями и спросить чего им не хватает в продукте.

Подсчет CAC (Customer Acquisition Cost)

Теперь Васе было понятно, что расходы на рекламу и продвижение нельзя игнорировать. Потратив $1000 на маркетинг и получив лишь 10 новых подписчиков, Вася увидел, что каждый новый клиент обходится ему в $100. С такими показателями бизнес не мог быть прибыльным даже теоретически.

Расчет Окупаемости (Payback Period)

Изучая принципы создания юнит-экономики, Вася познакомился с понятием COGS (Cost of Goods Sold), которые включают в себя прямые затраты на обслуживание одного клиента. Например серверные расходы, поддержку, лицензии и т.д.

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

Зная переменные затраты, можно рассчитать маржинальную прибыль, которая затем участвует в формуле для расчета срока окупаемости клиента. Маржинальная прибыль это разница между доходом и переменными затратами на одного пользователя (COGS). Рассчитывается как ARPU минус COGS. Например, если ARPU $10, а COGS $2, то маржинальная прибыль составит $8.

Остался последний шаг, посчитать окупаемость, то есть время необходимое для окупаемости затрат на привлечения. Рассчитывается как CAC, делённый на Contribution Margin.

У Васи получилось: $100 / $8 = 12.5 месяцев. У него потемнело в глазах, ему пришлось на время переключиться, чтобы осознать невыносимую легкость бытия. Он пошел проветриться, заодно купить хлеб.

Сравнение CAC и LTV

Пожалуй, это был самый болезненный момент для Васятки. CAC в $100 значительно превышает LTV в $25, а это означает, что каждый новый клиент приносит ему убытки. Если бы он продолжил в том же духе, деньги закончились бы очень быстро, и стартап пришлось бы закрывать.

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

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

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

Ссылки: Телеграм | Youtube | VK
Ловушки обучения

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

Меня неправильно учат

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

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

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

Я все понял

> Все понятно, но как дело касается практики — прихожу в ступор (студент Хекслета)

Эффект «я все понял» проявляется невероятно часто у подавляющего большинства людей. Когда мы смотрим или читаем теорию, нам кажется, что мы все понимаем. Но как доходит до практики, выясняется, что ничего не получается. И на поверку оказывается, что «я ничего не понял».
Существует множество приемов, помогающих определить ваше реальное понимание изученного, например:
• Попробовать сделать это самостоятельно на практике
• Рассказать своими словами другому — так, чтобы он тоже понял (будучи автором курсов, я учусь сильнее, чем учатся мои студенты)
Пока вы не проверили «понимашку», считайте, что вы находитесь во власти эффекта «я все понял», которому доверять нельзя.
Кстати, по этой причине почти не работают большие теоретические курсы и книги в отрыве от производства. Вы можете пройти их до конца с ощущением понимания происходящего, но на практике это будет самообманом.

Я должен знать, как сделать правильно — до того, как начну делать

Вспомните школьные физику, алгебру, геометрию, химию. По этим предметам мы решали огромное количество задач, и никогда не наступал момент, чтобы можно было сказать «теперь я легко могу решить любую задачу» (в рамках известной теории). То есть, вы гарантированно знаете теорию, которая используется в задаче, но задача все равно не решается. И даже после десятков решенных задач, все равно находятся такие, которые не поддаются.
Любая задача в подобных областях — это больше, чем применение теории. Это включение многих видов мышления, помогающих разбить задачу на части, выделить в ней главное (абстрагироваться), найти закономерности, скомбинировать известные приемы. Я уже не говорю про то, что в процессе вы будете постоянно ошибаться и отбрасывать неверные варианты. Постепенно появляется «чутье», ошибок становится меньше, прямых попаданий больше.
Именно это и есть обучение. К сожалению или к счастью, другого пути нет. Важные выводы:

• Не существует единственно верного и тем более «правильного» пути. Всегда есть компромисс.
• Нельзя заранее знать «как правильно». Удачные решения — это фундаментальные знания + опыт предыдущих поколений + ваш собственный опыт (шишки).
• Попытка использовать существующее (возможно, подходящее решение) без глубокого понимания проблематики (самая частая ситуация) приводит к тому, что появляется ложное ощущение «я умею» (а не только «я знаю»). В своей практике постоянно наблюдаю подобную картину: программист с годами опыта попадает в ситуацию, где не работает привычный инструмент, теряется, и либо ничего не может делать, либо делает нечто совершенно непригодное к использованию.

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

Ссылки: Телеграм | Youtube | VK
Концепции в разных языках программирования, которые помогают лучше понять возможности кодинга как-такового. Многие из этих концепций сильно облегчают жизнь и кардинально меняют способы описания логики. Поехали =>

Uniform Function Call Syntax. Фича языков D и NiM, позволяющая вызывать функцию тремя разными способами: как метод, как функцию и как команду. Удобно для создания цепочек. Во всех случаях это всего лишь функция:


p.move(5, 7)
move(p, 5, 7)
p.move 5, 7


Actor Model. Основной язык Erlang/Elixir и куча либ в других языках. Способ организации кода в легковесные изолированные процессы, общающиеся друг с другом через сообщения. Нет шаред стейта, легко делать канкаренси. Настоящий ООП по Алану Кею.

Pipeline. В языках Elixir, F#, Clojure, Haskell да и пожалуй во всех фп. Позволяет заменять вложенные вызовы функций на плоскую цепочку. Древнейшая концепция, во всю используется в шеле. Мастхев для языков где функции правят балом.


promise
|> await
|> x => doubleSay(x, ', ')
|> capitalize
|> x => x + '!'
|> x => new User.Message(x)
|> x => stream.write(x)
|> await
|> console.log;


Currying (и частичное применение туда же) Из популярных по умолчанию в ядре в Haskell, Ocaml-подобных, там функции сразу такие. В остальных языках ручками. Очень сильно повышает выразительность и упрощает многие конструкции. Реальное осознание только через haskell

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

Pattern Matching (+ Destructuring). Фича ФП языков, которая адаптирована многими императивными. Позволяет полностью изменить принцип работы с условными конструкциями, они становятся не нужны, а сама обработка разных условий становится и проще и понятнее

Mixin (Trait). Фича Ruby, PHP, Лиспов. Имитируется в JS. Один из лучших способ обобщать какое-то поведение и добавлять его в существующие классы. Правильная альтернатива наследованию (в том числе множественному) и абстрактным классам

Открытые классы. Smalltalk, Ruby, Python, JS (прототипы), Kotlin (частично в extension methods). Возможность поменять любой существующий класс прямо в рантайме. Значительно упрощает, например, тестирование. Снижает зависимость от IoC. Проще фиксить либы. Опасная и мегамощная фича

Message Passing. Настоящий в Smaltalk, Ruby, Python, PHP. Это не просто выбор полиморной реализации, а возможность определить общую реакцию для всех явно не определенных сообщений. Открывать путь к динамическим методам, которые нигде не определены.

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

Multimethods. Clojure и остальные лиспы (через CLOS). Киллер фича, позволяющая делать мультидиспетчеризацию (не только по типу) при создании полиморфных реализаций. Значительно сокращает количество и сложность иерархий классов. Обобщает полиморфизм.

List Comprehension. Фича почти всех ФП языков + Python, Smalltalk. Удобный механизм адаптированный прямиком из математики, для описания преобразования коллекций. Заменяет мапы/фильтры/вотевер.

Monads. Тут все просто. Монада это моноид в категории эндофункторов 😄 В целом штука, позволяющая управлять потоком выполнения и делать с ним всякое интересное. Из крутого: Maybe, Either. Порекомендуйте крутую литературу для интересующихся плс!

Generator. Есть во многих языках. Позволяют создавать бесконечные коллекции благодаря тому, что они формируются не сразу, а генерируются в процессе обхода. Часто связаны с ключевиком yield. Значительно упрощают написание итераторов

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

Ссылки: Телеграм | Youtube | VK
Пост-знакомство

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

В разработке я с 2007 года. Начинал с php, потом ruby, clojure, js/ts (фронт/бек), java, erlang/elixir, python, увлечение функциональщиной (ocaml, haskell), тимлидство, управление людьми, стартапы, vim. Могу сказать что хорошо разбираюсь в бекенд фреймворках (в том числе писал свои и они в продакшене), orm, devops, тестировании, инженерной культуре за что меня приглашают в качестве консультанта или тренера в разные компании. В 2013 я присоединился к Рахиму делать Хекслет и вот уже 11 лет занимаюсь бизнесом, попутно прокачавшись в методологиях, преподавании и многих других страшных штуках. Теперь вещаю все это вам тут и на ютубе)

p.s. И исчо, у нас пока тут с вами нет чата, но я вижу желание общаться больше чем просто в постах. Поэтому предлагаю присоединиться, в наше Хекслет комьюнити, где уже 8000 человек. @hexletcommunity здесь можно поболтать и с опытными разработчиками и помочь новичкам, которые тоже тусуют в этом чате.

Ссылки: Телеграм | Youtube | VK
Как понимать критичность разных кусков кода при разработке и ревью. Что от чего зависит, где можно и нужно забить, а где нет. (твиттер-тред)

Глобально и немного грубо весь прикладной код можно разделить на три уровня: Домен – то где бизнес логика + хранилища (бд), Управляющий код – то что оперирует доменом + инфраструктурный код и Представление – любые выходные данные типа binary, json, html и так далее.

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

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

Как пример код формирующий jsx в React. Если его много и он тяжелый, то его лучше разделять, но нет смысла пытаться делать его максимально компактным и красивым. Сегодня код один, завтра другой, потом вообще все поменяли и выкинули

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

Теперь обратная сторона – Домен. Сущности и связи между ними – буквально ключевая вещь. Абсолютно весь остальной код зависит от этого, то как вы разбиваете приложение тоже зависит от этого. Важно определить с чем мы имеем дело и какие кейсы нас ждут впереди

Например на Хекслете у урока может быть только одна практика (o2o). Почему не несколько? Если сделать несколько, то сложность системы возрастет многократно. Проще исходить из того, что если одной практики мало, то значит урок надо разбивать. Ключевое влияние на всю систему

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

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

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

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

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

Простой пример – сортировка. Она может быть написана сколь угодно плохо, лишь бы работала. Переписать всегда успеем, если нас это устраивает по производительности. Кстати не могу не порекомендовать фантастический гайд https://optimization.guide/

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

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

Ссылки: Телеграм | Youtube | VK