Путь разработчика
Привет! Меня зовут Алексей Скоробогатый. В 2015 году я пришел в Lamoda на позицию разработчика. Сейчас я системный архитектор e-commerce платформы и по совместительству Technical Lead команды CORE. В этой статье хочу поделиться инсайтами, которые получил за эти 5 лет — в формате takeaways, с историями, мемами и ссылками на литературу.
Понравился формат — ставь плюсик в карму. Буду рад любой дискуссии в комментариях под статьей: вопросы, мнения, опровержения!
В Lamoda я пришел в команду, работающую над поддержкой и развитием системы обработки заказов. Ничего не понятно, но жутко интересно.
После маленькой, но амбициозной веб-студии, в которой я работал до того, меня впечатлило ощущение серьезности внутри большой компании. Выстроенные процессы разработки казались идеально отточенным механизмом. Строгое, но наставническое ревью кода от руководителя и членов команды — вещь необходимая для такой сложной и ключевой системы. Для меня долетали задачи точечные, затрагивающие буквально один-два файла, не более. Большая часть кодовой базы и поведение системы была скрыта от меня туманом войны.
Примерно через месяц я выполнил одну из первых задач, связанную с реальными изменениями в работающей системе. Суть ее сводилась к добавлению одного поля в отчет по возврату денежных средств клиенту. Код-ревью, unit-тесты, тестирование релиза QA-инженером — все выглядело окей. Так как система была большая и сложная, релизились мы дважды в неделю, по регламенту — и вот в четверг моя задача уехала в продакшн. Большую часть дня релиз-инженер занимался сборкой и раскаткой кода с последующим компульсивным переключением между вкладками с графиками мониторингов, ошибок, очередей, логов — всего, что могло указать на неполадку. Но все выглядело отлично. Код слили в мастер-ветку и разошлись заниматься другими задачами.
Тишина в логах и мониторингах скрывала страшный баг: запрос к базе данных возвращал некорректное число строк. Итоговая сумма к возврату в несколько раз превышала реальную… Но об этом мы узнали только в понедельник. До сих пор помню, как устало и укоризненно смотрел на меня техлид, пока мы ехали в офисном лифте на следующее утро. Он до трех часов ночи отлавливал баг и готовил фикс для релиза. А компания понесла некоторый импакт от моей ошибки. Это был мой первый критичный баг, но далеко не последний. Люди ошибаются, и делают это постоянно.
Takeаway #1: Бизнес-процессы и данные на первом месте. Важно внимательно относиться к тем данным, с которыми работает система. Определять, с чем имеешь дело перед внесением изменений. Понимать контекст, в котором вносятся корректировки. Всегда рассматривать решаемую задачу с позиции контекста выше уровнем. Другими словами, ясно понимать, что происходит с точки зрения бизнес-процесса и кто является потребителем затрагиваемых моделей. Структура приложения может иметь сколько угодно слоев абстракции и разную степень качества самих абстракций, но это совершенно ничего не значит, если нарушена модель или бизнес-процесс в целом.
Я продолжал работать в той же команде, набирался опыта, и через полгода на командном стендапе бросил фразу, что в целом понял, как устроена наша система обработки заказов.
Конечно же я заблуждался.
Никогда не стоит недооценивать сложность больших систем. Очень хорошо про это сказал американский политик Дональда Рамсфельд:
… как мы знаем, есть известные известные; Есть вещи, которые мы знаем, что мы их знаем. Мы также знаем, что есть известные неизвестные; то есть мы знаем, что есть некоторые вещи, которые мы не знаем. Но есть и неизвестные неизвестные — те, которых мы не знаем, что мы их не знаем. И если взглянуть на историю нашей страны и других свободных стран, то последняя категория, как правило, является трудной.
Takeaway #2: Работая со сложными системами, важно понимать что мы знаем о них, чего мы не знаем, а о каком их поведении даже не догадываемся. И это не просто про инструментарий и следование тренду «Monitoring towards Observability», но также про управление зависимостями и оценку рисков при проектировании. Например, прежде чем решить использовать крутую трендовую базу данных для критичной системы, очень советую залипнуть вот на этом сайте boringtechnology.club
Через два года работы с системой обработки заказов я мог сказать, что уверенно знаю примерно 80% приложения. То есть про каждый модуль системы понимаю, как он устроен, и могу внести изменения. Знаю, какие бизнес-процессы отражены в той или иной модели, как они взаимосвязаны и влияют друг на друга. Провел интеграцию с системой процессинга платежей, которую спроектировала соседняя команда. Помимо интеграции, нужно было избавиться от наследия старого кода, так как раньше платежи были частью нашей системы — эта задача стала моим последним и самый масштабным рефакторингом крупного модуля. Все прошло настолько гладко, что даже неинтересно.
При этом внутри меня, как разработчика, назревал конфликт. Я искренне не понимал, почему наша система обработки заказов, столь важная для работы всего бизнеса, является такой хрупкой. Такими же хрупкими были и соседние крупные системы. Из всего полученного мной за два года работы опыта казалось, что какой-то надежности от сложных систем можно ожидать только при выполнении стандартных протестированных кейсов. А при попытке внести изменения, которых требует бизнес, все разваливается при первом резком маневре неудачливого разработчика.
Размышляя над всем этим, я наткнулся на статью Everything is broken, в которой автор пишет о той же проблеме, но еще более масштабно (и еще про то же, но под другим углом — Software disenchantment). Каждый раз меня будоражит, когда я нахожу со стороны подтверждение своим внутренним ощущениям — вот и в тот раз, прочитав статью, я наконец почувствовал, как мое смутное недовольство превратилось в яркий и явный инсайт:
Software is so bad because it«s so complex.
За примером в своей работе далеко ходить не пришлось: как раз в тот момент, добавив всего пару полюшек, мы на время полностью сломали создание заказа.
Наши большие и важные системы такие плохие, потому что они не помещаются в наши головы! А все бизнес-процессы, которые замыкаются внутри систем, не помещаются в головы менеджеров и аналитиков — и вообще нет такого человека, который понимал бы, как все это вместе работает.
Takeaway #3: При проектировании систем важно учитывать их когнитивную нагрузку. Она складывается из сложности технических решений, а также моделей и процессов предметной области. Хорошо спроектированные системы имеют высокую когнитивную нагрузку предметной области и низкую — технических решений.В идеале отдельная система должна иметь когнитивную нагрузку, которую может обработать один человек.
Окей, проблема понятна. Но предположим, у нас есть возможность переписать слишком сложную и потому плохую систему, упростив ее. На что еще нужно обратить внимание? В кибернетике есть теорема Конанта — Эшби:
Хороший регулятор системы должен располагать моделью этой системы. Good regulator
Смысл этой теоремы в том, что если мы хотим управлять каким-то объектом, нам нужна хорошая (точная и понятная) модель этого объекта. И чем сложнее объект или чем меньше о нем информации, тем труднее получить его хорошую модель —, а это негативно сказывается на управлении.
Думаю, мало кто не согласится, что все наши сервисы — это модели. Но что мы моделируем? Очень важно уделять внимание бизнес-процессам, моделировать не только состояние, но и поведение.
В конце 2017 года я перешел в новую команду CORE. Эта команда была сформирована тогда специально для выполнения задач IT — стратегии по декомпозиции монолитных систем. Основной целью мы ставили распил той самой большой, но хрупкой системы обработки заказов (закадровый голос: тогда самурай еще не знал, что у этого пути есть начало, но нет конца!).
Для меня это был новый опыт. Команда с совершенно иными принципами и образом мысли. Решения принимались быстро, были эксперименты и право на ошибку. Баланс вышел идеальный: мы пробовали и откатывались там, где импакт был минимален, но детально прописывали каждый шаг для критичных моментов.
Мы написали новый сервис создания заказов с нуля на другом языке (будучи php разработчиками, перешли на golang). Оценили первый результат и переписали еще раз. Упор был сделан на простоту и надежность. В центр поместили модель данных, а всю архитектуру построили вокруг. В итоге получился надежный и отказоустойчивый сервис. Нам удалось ввести его в эксплуатацию без отказов, используя механизм экспериментов. Со временем построенная система показала свою состоятельность не один раз.
Takeaway #4: All models are wrong but some are useful. Для построения корректных и устойчивых систем недостаточно моделировать состояния. Необходимо смотреть на поведение: паттерны коммуникации, потоки событий, кто ответственен за те или иные данные. Следует искать связи между данными и обращать внимание на причины этих связей.
У меня в университете был курс математического анализа, который вела доцент и к.т.н. Елена Николаевна. Она была очень строгой, но справедливой. На зачетах то и дело попадались задачи, для решения которых приходилось немного «покрутить» лекции — сделать самостоятельный шаг в сторону понимания материала. И на итоговом экзамене, который, к слову, я сдал со второго раза, пришлось проявить гибкость мышления и применить интуицию, чтобы решить задачу на «хорошо». Вот правило, о котором Е.Н. твердила нам весь курс, и которым я пользуюсь и через десять лет:
Когда не знаешь, что делать — делай что знаешь.
Вот почему я гордился, что знаю матан на «хорошо». Потому что по стандартам Е.Н. мало знать материал, но важно еще и понимать его, уметь синтезировать новое.
Takeaway #5: Чем дальше ты двигаешься, тем большую ответственность приходится брать и тем больше решений приходится принимать. В определенный момент абсолютная уверенность пропадает, как категория, но взамен приходит искусство баланса вслед за смелостью делать шаг.
Наступает время, когда вокруг тебя нет нужного человека, который мог бы снять существующую неопределенность. Приходится оценивать риски самостоятельно и брать ответственность на себя. Принимать решения в условиях неопределенности.
Во второй половине 2018 года наша команда лидировала проект «Подарочные сертификаты». Вначале я отвечал за разработку в процессинге и вокруг него. Позже, к концу года, ко мне перешло техлидство всего проекта вместе с задачей восстановить баланс сил после ухода части команды.
Существующие в голове правила и мироустройство разработчика трещали по швам, а потом окончательно рухнули. Ответственность за крупный и сложный проект выбили из меня идеалистические представления о мире разработки с понями и радугой. Жестокий мир ограничений и just enough решений требовал понимания и пересмотра всех подходов и правил, которыми я руководствовался.
Takeaway #6: Синдром самозванца. А что если меня разоблачат? Конечно разоблачат, если ничего не делать. Если делать важное, то через время замечаешь, что разоблачать тебя уже некому.
В соответствии с хронологией моего «Пути разработчика» здесь должна быть интересная с технической стороны история про проект персональных политик. В этом проекте мы реализовали обработку данных в реальном времени, и «на лету» изменили сами принципы архитектуры системы, перейдя к Events Driven Architecture. Но про это у меня уже есть отдельный доклад с конференции Хайлоад »19 (и статья тут на Хабре). Поэтому лучше я расскажу про «высокие материи» технического и не очень менеджмента.
Когда разработчик дорастает до позиции сеньора, что следует читать как «готов брать ответственность и умеет принимать решения автономно», то следующая классическая ступенька — это тимлид. Тимлид — это человек, который в первую очередь отвечает за команду, т.е. за людей и процессы разработки. Заказчик не приходит к разработчику, он приходит к тимлиду, и спрашивает по обязательствам тоже с тимлида.
Получается парадоксальная вещь — только разработчик дорос до того, чтобы самостоятельно работать как инженер, как его бросают в шторм под названием менеджмент.
Нет, возможно для кого-то такой путь представляется вполне комфортным, и переход от предельно однозначных алгоритмов и протоколов взаимодействия компьютерных систем к координации группы людей выглядит логичным. Но мне кажется, неспроста большинство разговоров в профильных чатах и на конференциях для тимлидов крутится вокруг понятия «боль».
В чем боль тимлида? Разве не в том, что инженер занимается менеджментом?! Нет, почему так происходит, понятно — школы технического менеджмента у нас нет как таковой, и предполагается, что инженер в ИТ — это супермен, который может разобраться во всем, включая и такую «простую» вещь, как менеджмент.
Но я решил пойти другим путем и в качестве следующей карьерной ступени выбрал позицию техлида. Как архитектор, я работаю с командами разработки, и теперь от ребят слышу то, о чем еще год назад сам говорил менеджерам:
Почему требования так плохо проработаны? Решения костыльные! Какие две недели?! Тут работы на месяц.
Но эхехей, теперь это моя задача решать такие проблемы. Но как только ты переводишь свое мышление в парадигму cost & benefit, то понимаешь, что все эти проблемы невозможно решить — you bout dat life!
Takeaway #7: Открытие! Менеджеры не занимаются решением проблем, они управляют беспорядком.
Моя задача, как технического лидера, это снимать неопределенность для команды разработки. Требования не проработаны? Решения костыльные? Архитектура не предусматривает? Это все сигналы хрупкости системы и расходимости.
Допустим, постановка задачи в сервис создания заказов выглядит так:
Необходимо добавить поле X и поле Y. Требуется, чтоб значение в поле Y» на выходе равнялось значению Z, если X равен 1.
Проблема кроется в самой постановке требований. Здесь ошибка в том, что совершенно не понятно какого состояния системы требуется добиться. Полностью определенные шаги в постановке приводят к неопределенности в процессе реализации и эксплуатации.
После нескольких подобных задач сервис создания заказов будет в достаточно хрупком состоянии — и начнут случаться такие кейсы, как тот самый, когда мы добавили пару полей и все сломалось.
Задача: обеспечивать сходимость состояний систем, консистентность постановки задач и уменьшение неопределенности для достижения устойчивости.
Люди, работающие над линией представления, постоянно строят и обновляют свои модели того, что лежит за чертой. Эта деятельность имеет решающее значение для устойчивости интернет-систем и является основным источником адаптивного потенциала. Above the Line, Below the Line
Архитектор должен понимать единство социотехнической системы. Уметь координировать процессы выше линии представления, чтоб системы ниже линии представления отвечали ограничениям по корректности, устойчивости и адаптивности.
Takeaway #8: Если правила перестают работать, поздравляю, вы добрались до граничных условий, при которых текущая модель перестает работать. Самое время пересмотреть свои представления и подобрать новую модель, которая будет отвечать текущим ограничениям и позволит выстроить адекватные процессы и правила.
Нет, правда. Именно это было написано в одной книге по архитектуре. И, по ощущениям, чем дальше, тем чаще я повторяю за этой книгой.
Технические концепции, алгоритмы и стандарты понятны — нужно лишь взять и последовательно разобраться. Я не пытаюсь обесценить труд инженеров — алгоритмы для распределенных систем чрезвычайно сложны, если вы на ежедневной основе не занимаетесь построением таких систем. Но чаще всего основная сложность, с которой мы сталкиваемся в процессе работы, возникает, когда требуется понять, зачем в том или ином сервисе требуется такой уровень абстракции для предметной области. И проблема часто усугубляется тем, что человека, который писал сервис, нет рядом.
Простые в реализации алгоритмы более успешны, чем математически точные. Paxos математически точен, но только с описанием протокола Raft, который проще в реализации, практическое применение алгоритмов консенсуса получило развитие.
Язык Golang ругают за его излишнюю ограниченность. Но именно на нем написаны Docker, Kubernetes и многие другие распределенные системы. Корректно выстроенная система ограничений служит фундаментом для успешных систем.
Все было бы гораздо проще, если бы технологии не должны были считаться с человеческим фактором. Но им приходится. Любая система в ИТ, в построении и поддержке которой участвует более одного человека, должна учитывать человеческий фактор.
И тут возникают технологии на стыке software & people, призванные структурировать хаос и описать сложные взаимодействия. Domain Driven Design, Microservices, Agile — все они создают ограничения, в которых описываются принципы и правила взаимодействия. Появляются структуры, с которыми понятно, как работать. Но далеко не всегда с приходом таких технологий становится лучше. Нередко получается наоборот — What Money Cannot Buy.
Takeaway #9: Программы могут и должны быть простыми. Для этого нужно прикладывать силы к формированию инженерной культуры. Именно она в конечном итоге определяет работоспособность сервисов.
Books
The Manager’s Path: A Guide for Tech Leaders Navigating Growth and Change, Camille Fournier — shop.oreilly.com/product/0636920056843.do
The Elegant Puzzle: Systems of Engineering Management, Will Larson — lethain.com/elegant-puzzle
Team Topologies: Organizing Business and Technology Teams for Fast Flow, Manuel Pais and Matthew Skelton — teamtopologies.com
Righting Software, Juval Lowy — rightingsoftware.org
Thinking in Systems: A Primer, Donella Meadows — www.amazon.com/Thinking-Systems-Donella-H-Meadows/dp/1603580557
Articles
Mental Models, Fernam Street — fs.blog/mental-models
Complexity Bias: Why We Prefer Complicated to Simple. Fernam Street — fs.blog/2018/01/complexity-bias
What Money Cannot Buy, LessWrong — www.lesswrong.com/posts/YABJKJ3v97k9sbxwg/what-money-cannot-buy
Becoming Unusually Truth-Oriented, LessWrong — www.lesswrong.com/posts/8BX7Do5epLdWz7uyG/becoming-unusually-truth-oriented
Programming Laws and Reality: Do We Know What We Think We Know? — www.drdobbs.com/architecture-and-design/programming-laws-and-reality-do-we-know/240166164
No Silver Bullet Essence and Accidents of Software Engineering — worrydream.com/refs/Brooks-NoSilverBullet.pdf
The Art Of Troubleshooting — artoftroubleshooting.com
From Fragile to Antifragile Software — developers.redhat.com/blog/2016/07/20/from-fragile-to-antifragile-software
Computers can be understood — blog.nelhage.com/post/computers-can-be-understood
Tuckman Was Wrong! (About teams) — medium.com/@DocOnDev/tuckman-was-wrong-e35e29716912
How to fail at almost everything and still win big — shime.sh/summaries/how-to-fail-at-almost-everything-and-still-win-big
Simplicity Before Generality, Use Before Reuse — medium.com/@kevlinhenney/simplicity-before-generality-use-before-reuse-722a8f967eb9
Simplicity, Please — A Manifesto for Software Development — www.infoq.com/articles/simplicity-manifesto-development
Software Design is Human Relationships — medium.com/@kentbeck_7670/software-design-is-human-relationships-part-1-of-3-perspective-1bcd53855557