Почему всё ломается даже у хороших программистов? Часть 2/2
После несерьёзной статьи на серьёзную тему Job Safety Driven Development стоит рассказать о том, почему даже опытные и добросовестные программисты волей случая могут попадать в схожие ситуации. Сначала захотелось написать, почему программисты ошибаются вообще ("Почему ошибаются программисты?" Часть 1 и Часть 2), но оказалось, что это слишком разные темы. Потом оказалось, что и на эту тему получился очень длинный текст. Пришлось разбить его на части. В первой части мы рассмотрим обычные случаи, которые знакомы многим крупным компаниям. И дополним понятие «серебряная пуля» понятием «золотая шестерёнка». Во второй части поймём, какую цену вам, скорее всего, придётся заплатить за «золотую шестерёнку», я приведу немного своего опыта. Как всегда, попробую писать простым языком, понятным широкой аудитории.
Меня зовут Константин Митин, я сооснователь и руководитель компании АйТи Мегастар/АйТи Мегагруп. Когда-то был простым разработчиком, работал в L3, дорос до тимлида, затем и до руководителя филиала разработки крупной ИТ-компании. Теперь я в АйТи Мегагруп.
Если вы ещё не прочитали первую часть статьи, то прочитайте её, пожалуйста. Золотые шестерёнки это не какие-то плохие либо злонамеренные люди. Это комбинация факторов из внутреннего устройства крупной организации и личных качеств человека.
Золотые шестерёнки, цена
Всё хорошо в меру, иногда что-то слишком хорошее становится уже плохим. Сахар и соль делают нашу еду вкуснее, но их переизбыток иногда просто не позволяет есть. Золотые шестерёнки представляют собой эффективный, но опасный инструмент. Они не только могут писать классный и сложный код, они ещё могут решать сложные задачи, в том числе и бизнес-задачи. Но их всегда нужно стабилизировать сильной командой и иметь аварийный механизм сброса. Золотая шестерёнка обязательно вылетит и разнесёт всё на своём пути, весь вопрос, куда она полетит.
Обычно всё начинается хорошо. В вашей компании появляется разработчик, который понимает бизнес чуть ли не с полуслова, быстро и эффективно решает задачи. Скорее всего, он привлечёт в вашу компанию сильных людей и сформирует вокруг себя профессиональную команду.
Но есть два негативных момента. Первый — этим начинает пользоваться средний менеджмент. Что такое средний менеджмент — это управляющая административная прослойка между бизнесом и исполнителями. Представители бизнеса не теряют свою адекватность, благодаря обратной связи от окружающей среды. Они свои ошибки быстро ощутят по финансовому потоку. Исполнители, которые работают в поле, тоже имеют непосредственную обратную связь с окружающим миром. Для разработчика это пользователи, внедренцы, техническая поддержка. Средний менеджмент не имеет ни того, ни другого. Они просто пишут отчёты и выполняют свой KPI либо его аналог.
Когда среднему менеджменту в руки попадает золотая шестерёнка, они начинают радоваться. Ведь с помощью этого человека можно выполнять свой KPI и все ошибки тоже можно на него валить. Иногда доходит до культа личности, в такие моменты начинается «это было его решение» и «если даже он не справился». Способ снять с себя ответственность и отвести от себя удар. На такого человека начинают отгружать всё больше и больше сложных задач, всё больше и больше ответственности за принимаемые решения.
Когда среднему менеджменту в руки попадает золотая шестерёнка, они начинают радоваться.
Второй момент — очень коварный. Золотая шестерёнка не ломается под нагрузкой, ломаться начинают люди вокруг и структура организации. Крепостное право отменили, люди устают и просто уходят. Иногда в команде золотой шестерёнки можно услышать: «Если ты уйдёшь, то я тоже уйду, я не согласен заниматься тем, что делаешь ты сейчас». Начинает снижаться взаимозаменяемость.
Комбинация этих двух факторов и даёт печальное явление «вылета золотой шестерёнки». Уход золотой шестерёнки равнозначен уходу её команды. Она уходит либо до того как, либо вместе с золотой шестерёнкой, так как оставаться без своего предводителя в сотворённом средним менеджментом аду они не соглашаются. За собой они оставляют важный и работающий функционал, код которого компактный и эффективный. Но именно потом про этот код скажут, как про «написанный непостижимым божественным разумом, который нельзя менять простым смертным» либо «написанный чужими для хищников» или «гуманоидной логикой». В общем, весёлых эпитетов от людей, которые принимают такое тяжёлое наследство, услышать можно очень много. Даже если в коде достаточно комментариев и есть документация.
Иногда проще сказать: «Мы не можем», распилить эту сингулярность на несколько разных команд, после чего реализовать раз в дцать больше кода, который будет делать то же самое, медленнее и хуже, но его смогут развивать рядовые программисты.
С механикой процесса я знаком на собственном опыте, потому что сам был на месте золотой шестерёнки. Чтобы не задевать ничьих интересов, на свой опыт и обопрусь.
Личный опыт
Мне довелось работать в одной крупной компании, реализующей облачный сервис в виде web-приложения. Когда я начинал там работать, в компании уже было больше тысячи программистов, сейчас их больше нескольких тысяч. Объём функционала — действительно большой. В компании был выделен отдел по разработке платформы для бэкенда и отдел по разработке платформы фронтенда.
Платформа фронтенда любила внезапно всё менять без сохранения обратной совместимости. В целом, соседние модули тоже этим грешили. Как результат, каждый релиз на продуктив выливался для всей компании в авральный багфикс. Примерно раз в один-два года платформа пробовала переписать всё заново, чтобы стало всё классно и хорошо. Конечно, классно и хорошо не становилось.
Если вас интересует, чем реально может заниматься орда программистов в несколько тысяч человек, у меня есть для вас ответ — пытаются костылями закрыть ошибку, которую допустил другой программист и система на это забила.
И если говорить честно, то увиденное у моего бывшего работодателя не является чем-то уникальным. После этого мне довелось посмотреть на то, что творится внутри других крупных компаний. Там всё хуже. Совсем наглядно стало, когда бывший сотрудник из моего филиала разработки (их у компании было больше 10), который пришел к нам стажёром и вырос до ведущего разработчика, ушёл в одну очень и очень большую ИТ-компанию. Пока он работал на старом месте, он страдал от бардака вокруг и ругал платформу. На новом месте он занялся местной платформой. И повторил все «ошибки» той платформы, что ругал.
В самом начале моей работы ко мне пришёл менеджер, чтобы я по справочнику моего модуля, в котором могло находиться несколько сотен тысяч позиций, сделал поиск, результат которого нужно было отдать пользователю весь и сразу. У меня не вышло с первого раза объяснить человеку, что такое делать нельзя, потому что при нескольких десятках тысяч записей у пользователя просто браузер упадёт. Я ему даже показал, как падает браузер у соседней команды, которые дескать уже сделали такой функционал (там справочник был всего на несколько тысяч позиций). Это эффекта не возымело.
Менеджеру было всё равно, ему нужно было бездумно выполнить задачу, чтобы не получить нагоняй от генерального директора. В общем, я его в грубой и экспрессивной манере отослал от себя подальше. Что было в результате? После нескольких итераций обсуждений, когда люди наконец поняли, что так нельзя и что такого наша платформа не может ни на бэкенде, ни на фронте, но генеральному директору очень хочется, пришлось мне сесть и написать мегакостыль. То есть сесть и написать нетривиальный SQL-запрос (формат выдачи с неожиданной группировкой) с оптимальным планом выполнения (требование компании, за этим яростно следили, и на это были объективные причины), а потом банально подломить платформу на фронтенде (JS же, мы и не такое проворачивали), чтобы она наконец хоть что-то смогла. Ну и несколько сотен строк прикладного кода, куда без него. Костыль имел длинное имя собственное из множества слов, среди которых было всего два цензурных: «волшебный» и «единорог».
Менеджеру было всё равно, ему нужно было бездумно выполнить задачу, чтобы не получить нагоняй от генерального директора.
Платформа обещала в скором времени отдать функционал, который позволит всё это убрать. Вот только я не помню, когда он появился, когда они смогли. Через два года? Через три? Где-то в ожидании этого момента я несколько раз грустно смеялся, когда падала платформа на фронтенде, а костыль продолжал прекрасно работать.
Кстати. Про правильное наименование костылей. Как-то мне пришлось реализовать на фронте функционал сравнения двух строк на базе расстояний Левенштейна с выводом строки исходника и итоговой строки в тултипе с "подцветкой" изменений. Опять всем было очень надо, платформа не могла, да и не выглядело это опасным костылём.
Написал я прикладную функцию, куда-то её положил (не стал делать её библиотечной, костыль же) и спокойно её использовал. Как-то мы провели рефакторинг, в ходе которого переработали эту функцию и переместили её в другое место. У нескольких команд из соседних филиалов рухнул их функционал. На вопрос, зачем они такое сделали, ведь мы её как API никому не обещали, внятного ответа дать они не смогли. На моей памяти библиотечной функции в платформе так и не появилось. Зато появилась куча копий этого функционала по разным модулям. Люди сделали выводы и подстраховались.
На самом деле это был всего лишь маленький эпизод длинного пути, в результате которого родился волшебный метод API, который в одиночку осуществлял многопараметрический поиск по всему моему модулю и где-то пяти (либо десяти?) соседним модулям с богатым набором форматов выдачи данных и нетривиальных группировок. Причём это всё происходило исключительно быстро, так как под каждый случай собирался свой оптимальный SQL-запрос. Всё это счастье объяснялось «требованиями генерального директора». Код за авторством моего ведущего разработчика и меня лично.
На самом деле это был всего лишь маленький эпизод длинного пути...
Почему так вышло? Потому, что я всегда настаивал на соблюдении обратной совместимости и со мной можно было договориться об интеграции, я не считал её чем-то сложным. В мире, где большая часть команд постоянно не заботилась об обратной совместимости, мой модуль стал интеграционной шиной, как большой торговый город, стоящий на перекрёстке множества дорог.
Вообще, генеральным директором и его «самодурством» в компании было принято очень многое объяснять. Только это было не до конца правдой. Генеральный директор был человеком умным и в прошлом талантливым разработчиком. Моей команде пришлось как-то своими руками повторить то, что он лет 10-15 назад сделал своими руками для десктопной версии. Ну, я то его алгоритм понял (раза с третьего), мне решение понравилось. А вот техлиды и ведущие разработчики — нет, алгоритм слишком неочевидным был. Им просто не хватило масштабности и строгости мышления.
Вообще, генеральным директором и его «самодурством» в компании было принято очень многое объяснять. Только это было не правдой.
Самодуром он тоже не был. Просто он сам был золотой шестерёнкой, на которую вся компания пыталась валить все решения, чтобы снять с себя ответственность и не думать своей головой. А когда человек изо дня в день проводит от 16 и более встреч по разным техническим вопросам, к концу дня от усталости он может принимать решения любой степени неадекватности. Если вообще сможет что-то делать. Генеральный директор — мог.
На мой взгляд, это скорее «ловушка основателя», которую описал Адизес.
Нагрузка росла, мы даже один раз успели по моей инициативе сходить в «смертельный марш». Что это такое и как оно делается, хорошо описал в своей книге Эдвард Йордон «Путь камикадзе [Смертельный марш]». Я не смог смотреть, как наша компания теряет замечательный и новый рынок, который открывался благодаря изменениям законодательства в сфере розничной торговли.
В процессе этого многомесячного похода я понял, что руководители проектов в компании не то, чтобы не могут представить себе схему передачи данных того либо иного функционала, им на это глубоко безразлично. Они предпочитали думать головой генерального директора, а мне было его уже жалко, поэтому и схему, и общую архитектуру сделал я сам. И они радостно начали думать ещё и моей головой, так как к генеральному директору сложно попасть, а мне можно просто позвонить.
Мой модуль опять стал точкой интеграции множества систем. Ведь наиболее быстро я мог менять свой функционал, а времени было до обидного мало.
Тогда мы успели и заняли какую-то долю нового рынка. Самым важным было зацепиться. Через какое-то время крупные компании вытеснили мелких игроков и увеличили свою долю. Мы были крупной компанией.
Сейчас начнётся самое интересное. Важно будет понимать, что я выступал сразу в нескольких ролях. Я руководил филиалом (центром) разработки в Новосибирске, это было обособленное подразделение со многими десятками программистов. Нам было тесно в старом офисе (почти целый этаж в одном из БЦ), практически перед моим уходом компания под филиал купила отдельное здание. Без этого филиал уже не мог развиваться дальше.
Весь найм сотрудников шёл через меня. Адаптацией сотрудников, фактически, занимался тоже я. В филиале было много отделов, в каждом из которых была команда от 4 до 8 человек. Один отдел принадлежал лично мне. То есть я возглавлял одну команду и половину своего времени ещё и код писал, выполняя роль играющего тренера. Таково было требование компании.
В какой-то момент моя личная команда разработки начала уменьшаться. Ведущие разработчики из неё начали уходить со словами: «Да ну это всё на фиг». Я знаю, что такое bus factor и почему он не должен равняться единице. Поэтому я всегда сам выступал дублирующим носителем знаний. Мог себе позволить.
Когда мой собственный bus factor стал равен единице, я начал поднимать тревогу. И дело не в том, что всем было глубоко безразлично. Сhief-уровень компании сказал «у меня просто нет других людей». А средний менеджмент этому даже обрадовался, это удобно для политических игр и для торговли между собой. Кроме того, чем больше я был занят, тем меньше успевал доставлять им проблем. Например, я не успевал заставлять их работать. Они, кстати, были вне моего подчинения.
Кроме того, чем больше я был занят, тем меньше успевал доставлять им проблем. Например, я не успевал заставлять их работать.
Прошёл год - безразлично стало и мне. Я подумал, что если я сам себя уволю, то уж точно не пострадаю от того, что мой bus factor равен единице. И если всем вокруг на это было всё равно, то зачем я переживаю-то?
В процессе этого всего мы сходили в ещё один «смертельный марш», чтобы вдохнуть жизнь в замерший функционал и выйти на новые рынки. Уже работали объединённой командой нескольких филиалов. Мне пришлось реализовать синхронизацию данных из облака с оффлайн-устройствами. Мастер-мастер синхронизацию. И опять по «требованию генерального директора» были нюансы. Бывает односторонняя синхронизация, бывает двухсторонняя, а у меня была «полуторная». Новые записи от оффлайн устройств принимались через волшебный метод типа «Найти или создать» с нетривиальной логикой работы. А после того, как оно создалось в облаке и потом приехало назад, приходилось зачищать дубли.
Это было временное решение, так как платформа обещала скоро выпустить нормальное решение для всех. Но пока они это не сделали, соседние команды начали синхронизироваться через мой функционал. Интересная задумка, прямо скажем.
В процессе реализации этого счастья внезапно выяснилось, что, когда я из базы данных получаю 64 мегабайта данных, мой поток на python падает из-за того, что у него заканчивается 2 гигабайта памяти. Вот так оптимально работал ActiveRecord от платформы на бэкенде. Кроме того, благодаря платформе на бэкенде методы API не могли принимать более 10 параметров, потому что кто-то в коде на C++ написал конструкцию типа: «Если передан 1 параметр, то … Если передано 2 параметра, то … если передано 10 параметров, то...». А мне нужно было больше 10. Вариант с JSON тоже не подходил, всё падало из-за памяти. Кроме того, версия базы данных была старой и там вместо JSON был доступен только hstor.
В процессе реализации этого счастья, внезапно выяснилось, что, когда я из базы данных получаю 64 мегабайта данных, мой поток на python падает из-за того, что у него заканчивается 2 гигабайта памяти.
Нет, я решил проблему через интересный SQL-запрос (с оптимальным планом выполнения), который возвращал мне hstor, из которого я делал строку, и ещё в SQL перегонял её в base64. Чтобы когда это придёт из БД на бэкенд, платформа не падала. Потом декодировал уже на python, подменял данные через замены текста в строке, затем опять закодировал (чтобы платформа не упала уже на передаче данных) и передавал на оффлайн устройство.
Я понимаю, всю нелепость и неправильность ситуации, а ещё глубокую сомнительность данных технических решений. Однако, мной было выписано множество ошибок на платформу. Они попробовали сопротивляться и отклонять их. У нас было несколько разборов на Сhief-уровне компании, в процессе которых мне удалось показать, что платформа работает неправильно. Где-то дошло до того, что я просто показывал скриншоты кода платформы на С++, где было сразу же видно, что оно работать не может.
Заведённые ошибки не исправили до моего ухода, прожили они больше года. В компании было правило, что командам с просроченными ошибками не давали премий. Сhief-уровень компании сделал исключения для команд платформы.
Кстати, на рынок мы успешно вышли. Бизнес-цель была достигнута.
Исправила ли платформа свои ошибки, я не знаю, ушёл раньше. Почему я ушёл. Потому что мне надоело решать организационные вопросы и потому, что у меня появились варианты интересней. Конечно, меня попробовали удержать, но я запросил столько, сколько мне не смогли дать.
Генеральный директор — человек смелый, он понимает, что незаменимых людей нет. Есть только стоимость замены. Я бы тоже себя уволил. Руководитель платформы кричал от восторга, когда я увольнялся. Наверное, я был чуть ли не единственным, кто мог заставить их работать. Среднему же менеджменту было безразлично.
Незаменимых людей нет. Есть только стоимость замены.
На самом деле, компания теряла только сильного разработчика и, возможно, его команду. В филиале оставалась очень сильная и слаженная команда руководителей. Потом, после того, как решатся проблемы с площадями, эта команда руководителей в неизменном составе отмасштабирует филиал разработки раз в 5. Этим достижением я горжусь.
Часть моей личной команды ушла со мной. Тот же чудный код синхронизации жил практически неизменным ещё больше года после моего ухода. Нетривиальное API модуля, кстати, тоже.
К чему же всё это?
То, чем занималась моя команда из 5 человек, попробовали отдать другим. Они не справились. Передали второй команде. Они не справились. Передали третьей команде. Они не справились. Пришлось пилить на куски и раздавать по нескольким командам. Путь полный боли и страдания длиной в год-два, не меньше.
Говоря честно, виноваты тут совсем не разработчики. Виноваты тут два управленца, которые не смогли договориться друг с другом по политическим мотивам. Ещё виноват средний менеджмент, который неполноценно выполняет свои функции и занимается локальной оптимизацией, решая свои политические задачи.
Говоря честно, виноваты тут совсем не разработчики.
Но именно в руках разработчиков потом всё и рассыпалось. Именно им пришлось что-то делать со сложным и непонятным кодом. Причём не зная истории его создания и не видя кучи взаимосвязей, ведь все носители знаний ушли. У менеджеров тоже знаний не осталось, они же предпочитали все хранить в моей голове. ТЗ они тоже не могли писать, я несколько раз заставил их это сделать, получилось очень плохо.
Научило ли это кого-то чему-нибудь? Да нет, конечно. У генерального директора действительно нет других людей, он работает с тем, что есть. А разработчики в компании и так более, чем сильные.
Подводя итоги
Конечно, разработчики могут ошибаться из-за своей неопытности либо скудоумия. Однако, часто в больших компаниях то, что мы воспринимаем как ошибку разработчика, происходит помимо его воли. Изменчивая внешняя среда диктует свои условия. Кроме программистов в разработке участвует ещё очень много разных людей. И не стоит надеяться, что какой-то разработчик решит организационные проблемы, с которыми не может справиться совладелец и генеральный директор компании. К сожалению, наша жизнь — это компромисс между нашими желаниями и нашими возможностями.
Отдельно скажу руководителям, что нужно опасаться работать с «золотыми шестерёнками». Они всегда вылетают. У них всегда bus factor равен единице. Они классные и по-своему талантливые, но берут за это высокую цену. Часто внезапно. У меня решений по аварийному сбросу из системы золотых шестерёнок нет.
Нужно опасаться работать с «золотыми шестерёнками», они всегда вылетают.
Если вы дочитали до конца и что-то для себя поняли, то спасибо вам.