Главный рефакторинг

Посмотрел вчера на код после джуниора, сделал всего один рефакторинг и тут же удалил 380 строк. Задумался, вспоминая последние 10..15 рефакторингов, что делал. По сути все они были одним и тем же: убийство копипасты. Нет, иногда копипаста не плохо. Иногда она даже нужна и является элементом архитектуры. Но часто она всё-таки фу и бяка. И один из skill’ов, что разработчик должен натачивать в себе с первого же молочного зуба — чутьё на плохую копипасту и практику её уничтожения. Если этого нет, нет и хорошего разработчика, даже не надейтесь.

Более культурно это нынче называется DRY (Don’t repeat yourself) и за последние лет двадцать каждый уважающий себя учебник рекомендует следовать этому принципу (ещё до того, как его сформулировали).
Например, [Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts. Refactoring. Addison-Wesley, 1999]: «Number one in the stink parade is duplicated code. If you see the same code structure in more than one place, you can be sure that your program will be better if you find a way to unify them».
Например, [Steve McConnell. Code Complete. Microsoft Press, 2004]: «Duplicated code almost always represents a failure to fully factor the design in the first place». (на деле у него примерно такой же текст уже в 1998 году был.
Например, [Robert C. Martin. Clean Code. Prentice Hall, 2008]: «Every time you see duplication in the code, it represents a missed opportunity for abstraction. .. Find and eliminate duplication wherever you can».

Почему это важно? Даже вот не с позиции абстракций и прочего, но сугубо прагматично кодерски.
Во-первых, вы мешаете JIT’у и прочим оптимизациям. Если исходник в одном месте и часто вызывается, у него гораздо больше шансов на автоматическую оптимизацию, чем у того же исходника в двадцати местах с размазанными вызовами.
Во-вторых, вы мешаете коллегам изучать код не только увеличенным объёмом текста и пустыми ожиданиями «раз оно двоится, может, не просто так», но и тем, что в IDE не получится тыцнуть в один метод и найти его использование.
В-третьих, вы очевидно мешаете исправлять этот код. Я и в самом деле сегодня вынужден был заменить 20+ копипаст. Предположим, правил бы багу. Мне пришлось бы править её в 20+ местах, после чего генератор копипасты был бы упомянут в разных позах и разных занятиях. Не говоря уж о том, что в случае баги я мог бы в одном-двух местах её и не исправить, пропустив. В общем, беда.
В-четвёртых, вы мешаете делать рефакторинг. Довольно часто бывает так, что после выноса копипасты становится очевидным следующий шаг. А потом ещё один. Рефакторинг похож на падающие доминошки, после одной каки чистятся ещё десять и в конце красивый компактный понятный код. А когда перед глазами портянки, сложно сразу уцепить возможность улучшения.

В общем, дорогие джуниоры, учитесь это самое. Даже если у вас в телах if’а повторы, уже повод почесать репку. Чешите репку чаще, оно полезно.

Инфраструктура стартапа

// По другому поводу написал конспект современной инфраструктуры, но получилось достаточно цельно, потому и за эссе сойдёт.
Развитие инфраструктуры идёт в сторону «делать больше, платить меньше». В общем, как и везде на рынке технологий. Небольшие компании не могут позволить себе сотни тысяч долларов ежемесячно, да и большие давно стараются ужаться в подобных расходах (один лишь GitHub на 1K пользователей в год начинается от 15М рублей), потому прогресс идёт вполне ожидаемым путём.
Под инфраструктурой далее подразумеваю всё, что обеспечивает процесс разработки и эксплуатации продукта, а не части самого продукта. Потому, например, Nginx в тренде, но это не инфраструктура.
Упомянутые названия считаю современным [хайповым] трендом. Иначе говоря, если собрать в небольшую толпу десятка два 20..30-летних разработчиков, из толпы будет доноситься то, что опишу ниже. Безусловно, существуют организации, живущие на другом софте, но речь не про них.
Каталог ниже также можно рассматривать, как первый набросок для первых обсуждений в начинающем стартапе. О, у нас есть N копеек и два человека, что мы можем из этого соорудить, чтобы продержаться десять лет до первого миллиона копеек? Если с этой позиции смотреть, так нынешний софт в какой-то мере представляет собою конструктор Лего. Кубики умеют работать друг с другом, редко являются обязательными, конструкцию по мере нужды можно наращивать и даже местами заменять с адекватной миграцией данных, ежели руки прямые. Я как-то прикидывал, нынче при действительно прямых руках можно чуть ли не всю инфраструктуру размазать за $150 в месяц. Не на сто человек, да, но мелкий коллектив вполне. Хорошие времена.

Нулевой круг: хостинг особо упомяну.
В тренде аутсорсинг и облачность. Соответственно, массово выбирают тех, кто на нормальном уровне за нормальные деньги предоставляет все те услуги, которые предоставлялись бы собственной серверной с отрядом админов, железячников, ноков и завхозов. Влёт можно назвать [в порядке убывания универсальности] Amazon AWS, Microsoft Azure, Google Cloud Platform. Это очень разные платформы со своей спецификой каждая, потому равными назвать не могу. Просто разные. Если нужно «в два клика» что-нибудь захостить, есть DigitalOcean. Сервисов на порядок (если не два) меньше, чем у того же AWS, но с задачей справляется.

Первый круг: VCS и Issue tracker.
VCS. Исходный код и прочее надо где-то хранить и обеспечить общину возможностью совместной работы с ним. Совместная работа нынче подразумевает активную вовлечённость, социальность, частично геймификацию, интеграцию со всем на свете. В тренде GitHub, GitLab, Bitbucket. Все три решения сделаны поверх git.
Issue tracker. Весело, молодо и задорно жить без тикетов или обходиться цветными бумажками на доске, но рано или поздно, а команда из этих штанишек вырастает. Задачи надо где-то описывать, обсуждать, отслеживать статус, строить графы зависимостей, получать срезы состояния и прочее. Тут устроить зоопарк проще, т.к. каждый год кто-нибудь пытается повторить успех JIRA (ведущий ледокол рынка трекеров, с каждым релизом бьющий рекорды неудобства). В спину ей дышат Bugzilla (для совсем олдскульных), YouTrack. С ощутимой натяжкой можно упомянуть Trello, но они всё-таки про другое. Но если далеко от системы карточек на доске уходить не хочется, то и Trello сойдёт.

Второй круг: общение, мониторинг и логи.
Общение. Команды часто распределённые (особенно если вы стартап и вам дешевле нанять двух senior’ов из Мордовии, чем одного московского middle), потому тема «где нам обсуждать работу?» поднимется быстро. Важной характеристикой решения является возможность видео на команду. Тут тоже бардак, т.к. долго не было единого продукта, решающего все задачи разработки (закрытые групповые каналы, архив, конференции, plugin’абильность, модные боты и т.п.). Потому у вас сейчас два варианта: устраивать конструктор из Skype / Jabber (ссылка на протокол) / Telegram или использовать Slack. Особняком варианты для knowledge base — что-то для хранения коллективных знаний. Тут у каждого своё, хоть одну из сотни Wiki поднимай, хоть один из вариантов по подписке, хоть закрытый репозиторий на GitHub. «Моделей» слишком много на любой вкус.
Мониторинг. В какой-то важный для команды момент количество тихо упавших сервисов начнёт действовать на нервы, после чего срочно вспоминают о нужде мониторинга живости машин и софта на них. Тут тоже не особо много боевых альтернатив. Частые метрики снимаются либо в Prometheus, либо в Influx. Событийный мониторинг есть смысл делать через всё тот же Zabbix. Приятные глазу графики как смотрели, так и будем смотреть в Grafana. А всякие стектрейсы и прочие ошибки можно отправлять в Sentry.
Логи. Весь софт пишет мегагигабайты логов, об которые ушатаешься ходить на каждую машину и грепать файл за файлом. Потому человечество придумало ELK stack (Elasticsearch, Logstash, Kibana). Держать в инфраструктуре эту триаду — та ещё головная боль, но иметь возможность удобно оперировать логами с кластеров… бесценно.

Последний круг: Continuous integration, Continuous delivery и остальное.
Темпы современной жизни диктуют темпы и разработке. Продукты надо делать быстро. Скорость часто бьёт по качеству, потому элементы CI/CD включают в себя также и максимальное исключение человеческого фактора — автоматизация проверок кода, автоматизация сборок, выкаток. В этой области раздолье и зоопарк полный. На деле же основной проблемой будет совсем не софт, но внедрение разработчикам в головы нужды в CI. Например, там тесты писать надо, а это же таааак скуууучно.
В этом же разделе решения, которые могут примениться раньше, могут позже, могут прямо-явно не относиться к CI/CD, но здорово его облегчают. В общем, список того, чем занимаются, когда хотят сделать всё правильно и есть возможность.
Контейнеры. Удобнее куда-нибудь доставить и где-нибудь поднять уже готовое приложение со всеми конфигами, файликами и прочим. Как минимум, это быстрее. На сцену выходит Docker, царь контейнерного хайпа. Лишь избранные знают и думают о других решениях. На самом деле вполне стоит и подумать, т.к. Docker не панацея и для ряда задач имеет смысл прикинуть альтернативы.
Оркестровка. Нравится это слово в контексте «хорошо, давайте управлять всеми этими контейнерами на этих инстансах в этих облаках». Тут поле боя, на котором рубятся Swarm с Kubernetes. Правда, тут снова тема Docker, но чё уж, мода. Сюда же Nomad для спартанцев.
Сборка и тесты. Так лаконично свёл CI в две задачи. На деле, конечно, решается гораздо больший спектр. Навскидку тут можно назвать три продукта: Jenkins, TeamCity, Travis CI (ваш выбор, если open source). Каждый со своими чудесами, своими лицензиями и ценами. Но вряд ли вы обойдётесь без них при внедрении CI в инфраструктуру. Писать скрипты запуска и сборки по крону занятно, спору нет, но лучше не надо.

Уверен, я не назвал десятки решений, которые вы считаете очень важными или лучшими или ещё какими-нибудь. Это нормально. Их назовёт кто-нибудь другой. Часть из них я просто не знаю. Часть не подходит небольшим командам (у вас целый департамент админов, это круто). Часть… кхм (знаю бойцов, живущих в IRC, им норм). Наконец, 35 ссылок в одном эссе — пока рекорд тут, ставлю запятую.

Тимлид в трёх лицах

Одна из вечных тем — что такое [хороший] teamlead? И вечные споры и дискуссии. Как водится, у меня своя точка зрения. Тимлид — набор из трёх ролей, который может быть хорошим в одной ситуации и плохим в другой. Эта неоднозначность и небинарность является, как считаю, причиной споров.

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

  • Открыть рот и связно озвучить мысли так, чтобы было понятно, о чём речь. Без этого выше табуретки никак.
  • Открыть рот и звуками убедить кого-либо в чём-либо так, чтобы этот кто-либо понял, захотел, сделал. Первый шаг к тимлиду.
  • Подбор подходящих коллективу людей. Не запихивать тихонь в клетку с тиграми, не топить тигров в пруду тихонь.
  • Выслушивать. Очень важное умение, дающееся не всем. Слушать не себя, не перебивать, запоминать слова, интонации.
  • Объяснять, почему повышают или не повышают. Не самая приятная обязанность, но надо.
  • Донести критику или одобрение. Как, собственно, самому уметь принимать как одно, так и другое.
  • Управлять конфликтами. Они были, есть и будут. Люди не травоядные и некоторая доза конфликтов даже нужна. Задача психолога не допустить их перехода в системную проблему.
  • Определять человеческие факторы производства и понимать, что с ними делать дальше. Вася апатичен и вяло работает? Почему? Он изначально такой или есть внешняя причина? О, так он сова, а его запихнули в жёсткий утренний график. Давайте попробуем его на вторую половину дня. Попробовали, Вася начал отжигать напалмом и повеселел.
  • Внедрение мотивации в членов команды. Федя слабо работает. Если Феде доходчиво объяснить цели, выгоды и соответствие работы ценностям Феди, он будет работать сильнее. Или не будет. Феди всякие бывают.

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


Логистик (процессы). Понимание и выстраивание процессов производства. Спектр задач тут большой и включает в себя много всякого разного ранга: схема статусов у тикетов согласно проекту, элементы CI, распределение зон ответственности, протоколы безопасности, чеклисты реакции на мониторинг, организация нужных встреч, контроль планов и само планирование, и т.п., и т.д. Фактически всё, что является дорогой между пунктами A (задумка продукта) и Z (продукт в production’е).
По сути это навык организации, который начинается с самоорганизации. В какой-то мере тоже личностная характеристика в основе. У человека должно быть стремление к упорядочиванию окружающего, да и внутреннего. Чёт не верю я в раздолбаев, успешно спроектировавших и построивших сборочный цех софта. Подчеркну — речь о повторяемом процессе, а не про одноразовый [случайный] win.
Примеры типичных задач:

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

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


Специалист (разработка). Глубокое знание предметной области, контроль качества реализации продукта. Пожалуй, самая очевидная и самая распространённая роль из-за обычая брать тимлидов из среды разработчиков.
Хоть я ниже допускаю вариант слабого специалиста (и его компенсации) в тимлидах, но это больше теория. На практике всё сложнее. Если тимлид тотально слабее в матчасти своих бойцов, он не может выполнять чуть ли не половину функций — не научит, не проверит, не проконтролирует. Как если первоклашку поставить ведущим разработчиком на уроках матана девятого класса, много он там со своими счётными палочками наводит, ага.
Но есть ещё нюанс. По миру ситуация меняется, а вот на просторах xUSSR команды разработчиков всё ещё в массе мужские со всеми приметами мужского коллектива. Разработчики не будут уважать тимлида, если он не является в какой-либо смежной области заметно более сильным специалистом. Не примерно равным, но заметно более — это важно. Может, всё-таки и будут, но не как разработчика. А без уважения всё становится сложнее. Хочешь не только по трудовой быть тимлидом? Изволь спросонья озвучить лекцию по булевой алгебре и вариантах рекурсии. Нет? Удачи.
Примеры типичных задач:

  • Разработка софта. Если программист не программирует, он теряет квалификацию.
  • Определение стратегии технического развития. Примитивный пример: вводить ли в стек новый язык?
  • Определение квалификации членов команды. Надо понимать, можно ли давать Петрову задачу по проектированию схемы данных из ста таблиц или Петров не знает, что такое primary key (очевидно, сам тимлид должен быть на уровне задачи).
  • Подтягивание квалификации членов команды. Как быть ориентиром (наш Вася крутой, я хочу стать крутым, учусь у Васи быть Васей), так и микролекциями, литературой, дискуссиями, кодом и т.д.
  • Ревью кода и решений. Тимлид должен понимать и уметь в хороший код, чтобы контролировать создание оного членами команды.
  • Обеспечение или контроль инфраструктуры производства и продукта в рамках компетенции. Оптимально ли настроена база данных? Те ли типы инстансов в облаке? Куда и как складываются логи? Почему так долго собирается билд?

Простота роли в том, что её можно сознательно прокачать, если голова на нужной позиции. Сложность лишь количественная — очень много чтения, осмысления и экспериментов, потому «ах, да я во всём крут» не получится никак.


И вот тут проблема: людей, совмещающих в себе все три роли на высоком уровне, [почти] не бывает. Хотел бы сказать, что их вообще нет, но остерегусь, мало ли. Общение с компьютером с детства развивает специалиста, но не способствует росту психолога. Психолог развивает с друзьями модные soft skills, пока на полке пылятся так и не открытые учебники по программированию. Логистик рождается в муках опыта, наблюдений, размышлений и снова учёбы.
Я пока видел только два действительно работающих способа решения.
Во-первых, в Яндексе боевые пары, которые забавно напоминают ролями семью. Менеджер (часто девушка) — психолог+логистик, тимлид (часто мужчина) — специалист+логистик. Соответственно, вдвоём они представляют собою универсального лида. Менеджер (мама) утирает слёзы, смягчает конфликты, обеспечивает социализацию команды, организует логистику продукта, организует само понимание продукта (а то эти роботы без тени сомнения напишут двумерного коня в кирпичном эфире), служит посредником между командой и внешним миром, напоминает команде о пользователях, вообще душа и человечное лицо команды. Тимлид (папа) организует разработку проекта, карает и поощрительно трогает плечо, сам укладывается под танк на амбразуру авралов, укладывает других, корректирует планы согласно уровням разработчиков. Если сработались, результат офигительный. Если нет, всё идёт на дно и по дну, в чём и риск такого варианта относительно единоличной диктатуры.
Во-вторых, подбор команды под тимлид(а|ов). Если нет умения или желания возиться с психологией, набор зрелых взрослых людей без явных проблем с головой — это делает ненужным развитого психолога. Если слабый специалист в лидах, набор строго сильных разработчиков, которых не надо контролировать или выращивать — лиду достаточно не вмешиваться. Если проблемы с логистиком, набор опытных (сильные не всегда опытные, потому набор выше из другой оперы) разработчиков, они сами организуются — слабый логистик там же и обучится.

Стремящийся стать хорошим тимлидом понимает свои слабые и сильные роли. Плохой не понимает.
Плохой психолог устраивает показушные игры в «давай поговорим» или «вы можете подойти ко мне с любым вопросом». Хороший и так знает / чувствует, что происходит, а с вопросами к нему и без напоминаний подойдут. // Я, к слову, фиговый психолог, но все и так знают, что злобное бревно. Компенсирую тем, что пытаюсь выстроить процессы, при которых в психологе нет нужды — ясные правила поощрений, тщательный подбор адекватной команды, прозрачность всей работы всех и т.п.
Плохой логистик тащит всё хайповое как сорока в гнездо. Вчера у нас был водопад, сегодня скрам, завтра коворкинг на хакатоне с интенсивным митапом тренеров по TDD без ТЗ. Другой вариант плохого в силовом напяливании идеалистических схем на неподходящий коллектив. Да, отверстие круглое, а болт квадратный, зато я сильный. Хороший делает так, что именно эти люди делают продукты именно этой категории максимально эффективно с учётом ротации кадров, фазы Луны и законодательства Гаити. Если не получается, оставляет всех в покое и смотрит, какую схему выработает команда — сначала смотрим на народную тропинку, а потом на её месте асфальтируем дорожку.
Плохой специалист всем просто мешает. Арнольд два дня оптимизировал тяжёлый запрос к базе, а тимлид зарезал, так как не понял текст запроса (ридабилити пострадало! скорость нинужна!). Плохой специалист не может аргументировать решения, потому тупо насаждает свои субъективные вкусы. Хороший специалист не мешает команде, особенно если команда решает задачу по результату лучше, чем сам смог бы. Также он каждое своё решение объясняет так, что решение становится очевидным. В конце концов, если чего не знает, так и говорит: да хрен его знает, разберитесь сами, потом меня научите.

Безусловно, всё ещё сложнее на пару порядков, всегда есть исключения и всё такое. Но мне схема выше удобна простотой и практичностью модели, от которой я могу отталкиваться, не затрачивая недели на принятие решений или на понимание явлений.
PS. Тем, кто обратил внимание на то, что по тексту в Яндексе менеджеры девушки, скажу следующее (и это нередко упоминалось в беседах с коллегами) — в Яндексе [не]удивительно много умных, энергичных и красивых женщин. Мне приятно называть их именно девушками.

История CSSO, выводы

Так вот, выводы, коих я сделал множество. Отголоски вы можете найти в эссе этого блога, но корни в CSSO.

Во-первых, если вы не product owner, ничего вашего нет. Нет ваших решений. Нет вашего кода. Нет вашего продукта. Ни к чему не надо относиться лично. Сразу выстраивайте забор и действуйте профессионально. Профессиональная разработка — уведомить настойчивого заказчика о проблемах, убедиться, что мысль донесена и проигнорирована, зафиксировать факт и авторство, решить задачу максимально хорошо, пусть это задача категории «пилить сук, на котором сидим». Сук спилен, мы упали, лапки сломали. И что? Вы этого и хотели. Я вообще многое научился документировать хотя бы истории ради. И с большим интересом с тех пор смотрю на людей, которые ну прям настаивают на том, чтобы диалог никак не фиксировался.
Во-вторых, энтузиазм хорошо, но подменять им реальность не надо. Спустя пару лет я размышлял. Вот чем мы думали, представляя, что недавние верстальщики, едва перековываемые во фронтендеров, начнут понимать и писать не вёрстку на JavaScript? Да даже сейчас (спустя 8 лет Node.js!) встречаю множество фронтендеров, в упор не знающих и не хотящих знать Node.js. Им сайты делать, а не утилиты править. Хорошо, обнаружил Пупкин, что CSSO не так сжимает background. Один из ста Пупкиных даже до репорта дойдёт. Один из сотни дошедших в исходник посмотрит. Один из сотни посмотревших что-то там поймёт. Не потому, что тупой, но потому, что ИМ НЕ НАДО. Всегда, когда есть вероятность, что какой-нибудь аудитории не надо, считайте, что не надо никому.
В-третьих, ИМ НЕ НАДО. Нет, правда. В мире по разным оценкам от 10 до 20 миллионов разработчиков. Посмотрите список коммитеров проектов, которые вы считаете ну очень, очень популярными. На которые все должны молиться и каждую бажку забивать молотками за секунду репорта. Сюрприз. Каждый проект в пересчёте на масштаб делает очень небольшая команда людей, которым действительно не пофиг. Остальным пофиг. Всё, что вы будете начинать в одиночестве, вы будете делать в одиночестве, продолжать в одиночестве и завершать в одиночестве. Недавняя история MongoDB-драйвера на Go тому примером. Чувак 7 лет тянул на себе вполне хороший драйвер, все использовали, но драйвер закрылся потому, что чуваку надоело, а больше никого нет. Ха. Пользователей может быть миллион, но вы всё равно будете в одиночестве. Они потребители вашего продукта, не более.
В-четвёртых, чем выше порог входа вашего начинания, тем ниже вероятность соратников. Люди не читают, не учатся, не интересуются. Всё непонятное им кажется страшным. У CSSO были PEG и OmetaJS, у PhantomJS низкоуровневый код. При этом последний автор PhantomJS говорит:

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

Ойнемогу. Виталий Слободин, что раньше CSSO разрабатывался одним человеком, что сейчас. По факту два (!) человека за 7 лет жизни утилиты. А вы говорите. Не надо в прогнозах учитывать (мечтать, фантазировать) пассионарность. Надо учитывать её отсутствие и радоваться, если с ней встречаетесь.

В-пятых, проекты одного разработчика — это всегда зона риска. Его сбивает автобус и всё. Ему надоело и всё. Он укатил на Карибы и открыл там кафе и всё. Если вам нравится библиотека X одного разработчика, а библиотека Y группы разработчиков чуть хуже, берите вторую.
В-шестых, проекты из компаний (а не сообщества) — это всегда зона риска. Компаниями управляют бюджеты, иерархии, прибыли, что-нибудь ещё. Компаниями не управляют (вы всё ещё верите маркетинговым брошюрам и статьям?) другие категории. Вы пользуетесь библиотекой, всё хорошо. Во вторник руководитель 5-го ранга решает, что разработка библиотеки не приносит прибыли. В среду команда разработки расформирована. В четверг вы думаете о том, чего дальше-то.
В-седьмых, soft skills важны и нужны, если вы хотите добиваться своего. Я конфликтен, злобен, меня раздражают многие категории населения. История CSSO научила меня тому, что разработка такого уровня в таких условиях состоит из soft skills чуть ли не на 50%. Из чего я сделал вывод больше никогда не начинать подобное в таких условиях. Право говорить людям то, что ты о них думаешь, стоит неначатого, закрытого, пошедшего не тем путём. Наиболее правильная позиция у Линуса Торвальдса. Делает нужный крутой продукт, всех посылает в лес, никому не улыбается насильно вежливо. Уважуха. Помните абзац про моё недопонимание с фронтендерами? Вот здесь оно вставало порою в полный рост.

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

История CSSO, завершение

Особняком от разработки как прошлый 2011 год CSSOv1, так и наступивший 2012 год оказались весьма насыщенными общением. Сначала вы общаетесь с десятками людей, чтобы начать проект и делать. После публикации о проекте вы общаетесь с десятками людей, объясняя вводную, собирая первые репорты и вообще торгуя вежливостью и готовностью. После популярности вы общаетесь уже с сотнями (GitHub, Twitter, mail, лично, Jabber, даже пара звонков была) людей. Такой вот постоянный поток, который надо держать [и продолжать быть вежливым]. Объём его достаточен, чтобы пойти к руководству и рассказать, мол, смотри, я 20% рабочего времени не код пишу, не думу думаю, но просто отвечаю на вопросы, даю ссылки на спецификации, выясняю детали репортов и лучусь оптимизмом. Было несколько свежим опытом.

2012 год
Январь — целый месяц багфиксов, снятия накопившегося техдолга, организационные правки. В следующие месяцы бываю в CSSO лишь набегами в среднем два-три дня в пару месяцев.
Октябрь — CSSO перешёл с парсера PeCode на Gonzales. Gonzales был очень интересным экспериментом. Я попробовал написать руками парсер, который был бы выдан воображаемым генератором парсеров воображаемой PEG. И ведь получилось. Скорость разбора увеличилась в 3..5 раз, код с позиции генератора вышел стройный и хорошо алгоритмизируемый.
Фактически это был шаг к разработке следующего поколения экосистемы вокруг идей OmetaJS. Также это был последний камень в надгробие CSSOv1.
За два года идеи и эксплуатации стало очевидным, что это поколение себя изживает. Надо избавляться от PEG. Надо писать менее монолитный продукт. Количество набранного опыта структурной минимизации следовало переделать в новое качество. Наконец, в голове накопилось немало фантазий о том, какой [крутой] стек утилит вокруг можно выстроить. Но требовался другой парсер с другим AST. Так CSSOv1 был поставлен на логическую паузу и я начал домашнюю проработку CSSOv2.
Также время, которое мне разрешали тратить на CSSOv1, неумолимо сокращалась и проект по приоритетам опустился на самое дно. Надо было писать другие утилиты.
Всплыл ещё один нюанс, который надеялись перевалить умом и сообразительностью, но в рамках CSSOv1 так и не получилось — gzip. Иногда CSSO жал файлы лучше gzip, иногда хуже. DEFLATE хорошо себя чувствует на избыточности, что в случае с CSS подарок. Это дало повод в дальнейшем ряду критиков говорить, что CSSOv1 хуже gzip, от чего я меланхолично фейспалмил и ставил галочку в чёрном блокнотике.

2013 год
Март — моё категорическое нежелание продолжать работу с руководителем группы, в которой оказался, закончилось намерением либо уволиться, либо ротироваться, если найдутся желающие. Желающие нашлись, но уходил (возвращался) я в мир Java на совсем другие задачи. Так CSSOv1 был поставлен уже на физическую паузу.
Апрель — объявляю на внутреннем форуме Яндекса, что больше не могу поддерживать проект. Разработка CSSO велась в рабочее время, а не была ночным хобби. Мне ясны были чувства пользователей, но после 10-часовых рабочих дней потом ещё фигачить и такой непростой проект — нет. CSSOv2 был прикопан, участвовать в играх «проект важный и полезный, но ресурс на него мы выделять не будем» я не собирался.
CSSO стремительно устаревает. Я уже занимаюсь другими задачами, поддержки проекта почти нет (лишь редкие багфиксы и несколько релизов Gonzales), желающих подхватить знамя нет ни внутри, ни снаружи, пусть и были многочисленные призывы.
В этот же период постоянно слышу от людей о том, что такой важный и интересный проект обязательно вот сейчас-сейчас кто-то возьмёт на себя и всё станет хорошо. Были попытки войти в код CSSOv1 (но входящие быстро ломались), были единичные случаи фиксов, даже нашёлся разработчик, выдавший серию фиксов, чем меня восхитил и подарил [короткую] надежду. Но и только. Стало ясно, что в текущем виде CSSO доживает своё время.
В этот же период я получил здоровенную прививку от излишнего оптимизма, доверия и прочих штук. Вроде и так был злым, а тут стал ещё злее и циничнее. Тогда было неприятно от ряда ситуаций и диалогов, но сейчас понимаю, что всё закономерно и это просто надо было пережить, чтобы больше не попадаться.
Если 2011 год был годом победы, 2012 год годом поддержки и развития, то 2013 год стал годом «да ну вас нафиг».
Август — последний коммит.

2014 год
Разработка не ведётся. CSSOv1 умирает. Прикольный период танцев на костях. То одни бенчмарки показывают, что конкуренты (а надо заметить, после выхода CSSO этот «рынок» оживился и за минимизацию взялись с новыми силами) впереди-впереди, то другие показывают, что CSSO позади-позади. И вот уже с задних рядов улюлюкают, а с передних осуждающе смотрят в моноколь.
Тогда мне демонстративно было пофиг. Ну… Кажется прям совсем вот очевидным, что проект надо было развивать, чтобы он не остался в хвосте. И если проект (любой) не развивают, он устаревает. Для этого не надо быть семи пядей во лбу, это аксиома. Божечки, неужели после года-полутора-лет заморозки конкуренты впереди?! Да неужели! Да как же это случилось-то?! Если вам не пофиг, напишите петицию, вручите рулон подписей руководству, пусть выделит бюджет на разработчика. Одного хватало же. И сейчас один, к слову. Но гораздо прикольнее было кидать тапками, пока я ходил то к одному начальнику, то к другому, выпрашивая куски рабочего времени на хотя бы багфиксы.
Надеюсь, эта история чему-то полезному научила не только меня, пусть и выводы каждый участник сделал свои, безусловно.

2015 год
В октябре за разработку берётся lahmatiy (Роман Дворнов из Авито) и CSSO получил новую жизнь. Офигенно, что нашёлся кто-то, кто уже третий год тянет хороший же проект вперёд, во всём разобрался и всё переписал. Роман, если ты это прочтёшь, знай, ты крутой.

История CSSO, продолжение

Чтобы понимать всё дальнейшее в коде CSSOv1, надо принять следующее: за основу был взят OmetaJS и PEG. Регекспы спорны в поддержке, мы видели обработку CSS на их основе и глаз вываливается. Да и, напомню, нужна была совместимость на уровне AST.
Ни код, ни стоящие за ним идеи невозможно правильно понять, если не понимать, чем выбранная основа отличается от остального мира. Я потом неоднократно наблюдал, как приходит человек, смотрит исходник и убегает в ужасе с криками «что за бублец, это не код, это ад!» Да нет, не ад, но ведь даже Википедию сначала почитать ленились.
Потому основная работа с кодом всё время существования CSSOv1 делилась на две основных ветви.
Во-первых, парсеры CSS. На старте это не казалось прям сложным, но теперь понимаю, что задача «быстрый стабильный парсер CSS на JavaScript с учётом всех версий CSS и всех хаков CSS вместе с вендорными расширениями» — такое… При этом учитывайте, что V8 и Node.js тогда ещё не включали в себя нынешнее богатство оптимизаций. Именно эта ветвь стала самой интересной, проблемной и мозгоёмкой.
Во-вторых, поиск, каталогизация и изучение того бардака, что существовал и существует в вёрстке. Легко было выстрадать блок минимизации, обложить тюнингом, а после выпуска версии узнать, что в каком-то IE на какой-то платформе всё не так. Была переработана тонна информации, в курилках и вне оных пытались симферопольские фронтендеры (всегда с готовностью помогавшие, вспоминаю с благодарностью), обрабатывались репорты пострадавших. Шёл вечный бой.
Итак, до конца 2010 года велась подготовительная работа, которая в свою очередь разделилась на следующие этапы…
Во-первых, сбор, осмысление и описание того, что, собственно, собираемся минимизировать. Понятно, также должны были уметь то же, что конкуренты, потому и их методы были собраны воедино, а в дальнейшем и улучшены.
Во-вторых, писались разные прототипы. Надо было быстро обнаружить тупиковые пути и нащупать нормальные решения. Например, один из скриптов был создан только для того, чтобы посмотреть, как Node.js ведёт себя на тысячах и миллионах узлов дерева (фигово вёл, надо заметить).
В-третьих, собирался meta CSS — «язык», состоящий из всех спецификаций и диалектов CSS, ведь именно его и надо было парсить со всеми-всеми чудесами. Кажется, в какой-то момент во всём Крыму я был единственным человеком, который совершенно не умел верстать, но CSS знал чуть ли не лучше авторов.
Таким макаром пришли к точке, после которой уже можно писать продукт.

2011 год
Февраль — первый коммит работающего CSSP. Несколько наивная и прямолинейная реализация парсера, который даже в таком виде работал быстрее, чем созданный через OmetaJS. Тогда же было принято решение писать код, который с минимальными изменениями будет работать на всех JavaScript движках. Это позволило позже сделать и вебовую версию CSSO.
Март — интенсивное наращивание мяса на кости. В общем, ничего особо интересного или интригующего. Архитектура ясна, ТЗ понятное, прототипы обстреляны, бери и делай. Некоторое заметное время отобрали сотни тестов, в будущем себя это оправдало.
Апрель — релиз ранней беты и пост на Хабре. Приняли хорошо, дальше пошла рутина и началось общение с пользователями. Заодно пришла нужда отключать (!) структурные оптимизации. Если код, который минимизировал механически, работал понятно и без багов (потому его хотели использовать), то другой пока был с багами и открытыми вопросами.
Август — упоролся и написал пробный генератор парсеров. Назвал PeCode. CSSP себя изжил, с ним можно было запускаться, но в долговременной перспективе он был слабоват. Потому появился PeCode. Честно говоря, сейчас я уже не понимаю, что именно получилось. В одном проекте я проверял много разных идей, писал очень хардкорный код и в итоге решил, что ЭТО можно считать генератором. В любом случае он был быстрее и лучше CSSP, что двинуло нас вперёд.
Сентябрь — наконец-то CSSO действительно научился работать в режиме строго без структурных изменений. Месяц интересен ещё и тем, что он был последним в непрерывной работе именно над CSSO. Дальше перерыв на три месяца, я переключился на BEM plugin к IntelliJ IDEA.
Год был замечателен тем, что CSSO зашагал по планете. Его использовали на всех континентах, его начали пробовать в production’ах уровня Yahoo! и Google. Количество установок било всё новые рекорды. Чувство (и опыт) win’а такого уровня задают в дальнейшей жизни другую, новую планку целей. Могу совершенно точно сказать, что именно CSSO дал мне основу для развития и переосмысления. Ничего не было. Приходит один чувак с идеей. Приходит другой чувак с руками. Бац, сделали. Бац, сотни тысяч установок. Ы. Дикий Запад какой-то.

История CSSO, начало

В новостях похоронили PhantomJS, пошёл листать его историю, долистал до интервью, встретил CSSO и решил поднять старые записки. С каждым годом что-то забывается, а зря.
Наблюдаю за судьбой CSSO, мне нравится происходящее, но не нравится, что стартовую кодовую базу могут понять не так, как понимаю её я. Потому вот вам история. Как водится, это взгляд с одной стороны, с других сторон всё может выглядеть иначе (более того, не может, но и выглядит).
Для альтернативно читающих сразу обозначу следующее прямым текстом…
К той версии, что вы используете, я не имею никакого отношения. Она лучше CSSOv1, быстрее, написана иначе (фактически это на 95% другой продукт). Мой последний коммит был в августе 2013 года, после чего разработка временно заморозилась и продолжилась уже другими.
Эссе о том, почему CSSOv1 такой «странный», заодно байки о том как писался. Если вам покажется, что текст о другом, не надо меня об этом уведомлять, справляйтесь самостоятельно.

2010 год
Node.js существует всего год. Едва появился npm. Мира небраузерного JavaScript почти и нет. На этом фоне Виталий Харисов поднимает тему, о которой думает не первый раз (только лишь со мною была пробная попытка на C написать хоть прототип в 2007 году) — структурный минимизатор CSS. Популярные на тот момент минимизаторы представляли собою простые «выгрызатели пробелов», потому идея была богатая: в CSS овердофига избыточности, которую можно убирать, «понимая», какие свойства перекрываются, какие блоки объединяются и т.п.
Нюанс был в том, что 1) этим никто не занимался, 2) я чистый бекендер и про CSS знал лишь то, что это ужас и содомия.
Виталя добыл добро на разработку долгостроя, поставил технические требования, а я засел за спецификации, комбинаторику, теорию парсеров (в основном вокруг PEG) и трансляторов, ну и вообще стал активно вливать в себя чарующий мир вёрстки. После первых набросков стало ясно, что таки да, реализовать это можно.
В этот момент и были заложены архитектурные решения, которые сначала подняли CSSOv1, а потом похоронили. В итоге жарких споров решили, что AST должен быть удобным для OmetaJS (потому и упомянутая выше PEG) — на массивах. Эту точку считаю поворотной.
С продуктовой точки зрения решение верное. CSSO таким образом мог быть частью цепи преобразований, звеньями которой разные утилиты на базе OmetaJS трансляторов. Тут у нас валидация, здесь обработка метаязыков, там минимизация. С технической точки зрения такой AST как сам по себе стал тормозом, так и остальные операции тормозил. Мне не удалось тогда доказать это двум главным решающим фронтендерам, но какие сложные эмоции обуревали, когда потом уже другой разработчик в CSSOv2 напрочь выпилил совместимость, сделал нормальный AST и всё стало лучше.
Но… 2010 год. Можно сказать, что никто не пишет «серверное» на NodeJS, да и вообще JavaScript. Утилиты для фронтендеров похожи на свалку хлама в непонятном состоянии на разных языках для разных операционных систем. Какого-либо open source продукта, сделанного в единой идеологии, прошедшего кровавый production, состоящего из семейства полезных инструментов, просто не было. Фактически мы стояли на целине, что в разработке очень редко бывает.
Потому тогда сложно было сказать, что правильно, а что нет. Желания и энтузиазм точно были правильными. В эти настроения корнями уходят и будущие SVGO (начинал deepsweet в 2012 году) с IMGO (начинал banzalik в 2011 году), и первые утилиты для BEM, и многое другое. Так всё и завертелось. Виталя с коллегами вовремя увидел возможность, набрались разработчики, ну и целина начала вспахиваться.
Тут надо упомянуть ещё одну «тонкость», без которой фон некоторых решений в дальнейшем не будет полным. Код писался бекендером для фронтендеров на языке фронтендеров в стиле бекендеров. Хоть меня и убрали от живых людей и процессов (уже успел поругаться с важным в песочнице человеком), это нисколько не спасало от влетающих со стороны хотелок и поправок. Некоторые были хорошими, некоторые глупыми, но всё надо было учитывать и не всегда у меня получалось сделать правильный выбор или отстоять его. Также как по природной конфликтности, так и по объективным (как сейчас оцениваю атмосферу, причины и следствия) причинам разработка велась не всегда ровно — как тогда, так и сейчас мне непросто общаться с фронтендерами, это какой-то совершенно другой мир и совершенно другая школа разработки, от которой меня знатно морщит (подозреваю, им тоже не всегда было приятно иметь со мною дело). Запомните этот абзац, он будет особо упомянут в финальной части серии в выводах.

Три проблемы MongoDB

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

Во-первых, дисциплину схемы данных поддерживать крайне сложно. Разработчики должны постоянно помнить, что это не RDBMS, потому десятки связанных «внешними ключами» коллекций могут решить тактическую задачу, но в стратегической перспективе вы всё сделаете неправильно (особенно с учётом пока ещё отсутствия multi-document transactions, но в 4.x вроде запилят).
Фигня в том, что так или иначе, если вы не делаете простое или специфическое приложение, вы всё равно однажды скатитесь ровно к тем самым десяткам коллекций, что в MongoDB чревато — механизмы проверки целостности данных в зачаточном состоянии. Конечно, допускаю, что вас может оставить равнодушным потенциал продолбать связность чего-то вроде клиент-счёт, но обычно это разработчиков тревожит.
Соответственно, у вас всего один вариант хранить сложные данные: огромные документы с большим уровнем вложенности, в которых всё-всё. Казалось бы, ну и ладно, это же хорошо, это вот прям MongoDB way! Не-а, переходим к следующим пунктам.

Во-вторых, механизм локов. Реально проблема, когда дело доходит до лока на уровне документа (почти всегда). Если кратко без нюансов и минимально для понимания, во время изменения документ залочен целиком.
Представим, что вам хватило мудрости создать документ «дом». В котором «квартиры». В которых «жильцы». Если ваш сервис раз в час переписывает «дом» целиком, жить можно. Если «дом» меняется несколькими сервисами в десятки потоков из разных источников… Пока вы меняете семейный статус Петрова, запись рождения Иванова и удаление Сидорова из пятой квартиры будут ждать в очереди.
На графиках очень красиво выглядят пики, когда звёзды сходятся и при [условно] 100RPS на «дом» последние изменения накатываются через 10 секунд, например. Шутки шутками, но т.к. такие лаги аукаются во всём остальном, становится грустно.

В-третьих, как ни странно, но у MongoDB очень бедный язык запросов внутри документов. Они чуть ли не в каждом релизе улучшают и дополняют, но если вам надо получить квартиры, в которых за последние пять лет произошло возгорание на кухне, в котором участвовал Фёдоров с условной судимостью… Такое стоит неоднократно пережить на собственном опыте.
Зачастую вам проще просто найти «дом», вытащить его целиком и вне MongoDB пройтись по документу. Соответственно, получаем дополнительную нагрузку что на базу, что на код. Не ок.

Потому совет всё тот же: сначала делайте сервис на RDBMS. MySQL и PostgreSQL давно уже взрослые и крепкие базы. Когда сервис заработает, поживёт и вам станет яснее реальная нагрузка и вообще вектор развития, тогда вы сможете достаточно просто мигрировать на MongoDB. Вот мигрировать с MongoDB на RDBMS врагу не пожелаю.
При всём этом MongoDB неплохая база, если использовать её правильно. Плохо, что использовать её неправильно слишком легко.

Python для собеседований

Однажды задолбался задавать на собеседованиях одни и те же вопросы без fun’а, родил функцию на Python. Веселился, придумывая. Кажется, решавшие задачку тоже получали свою порцию.
Собсно, вот код (Python 3.x):

import 'os'

func readFileId(names=[], mode):
    _ = ''
    id = -1
    for n in names:
        with os.open(n, 'w') as f:
            _ += f.read()
        f.close()
    print 'default: ' + id + ', actual: ' + _
    return id ? _ : id

Надо найти в нём ошибки. В зависимости от упоротости и перфекционизма количество ошибок от X до Z (подумал и решил таки не публиковать числа, лишняя подсказка получается). Кому и игнор PEP8 спать не мешает.

Суть упражнения (вполне типового, похожим джавистов на сертификациях кошмарят) в следующем:

  • Проверка знания языка.
  • Проверка внимательности.
  • Проверка умения думать над кодом.
  • Иногда показывает уровень чувства прекрасного.

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

PS. Тем, кто не согласен и у кого что-то не получается: пжлст, не пишите мне, что вы не поняли код, что это плохое упражнение и прочее. Значит, не для вас. Не беда.