Хабрахабр

Все люди не умеют писать код

В преддверии Moscow Python Conf ++ мы поговорили с Никитой Соболевым, CTO компании «Мы делаем сервисы», о глобальной проблеме управления сложностью кода в разрезе развития языков программирования. А также о том, почему тут со временем ситуация становится только хуже. Плюс расспросили, зачем ему потребовалось создавать собственный линтер.

— Расскажи в двух словах о себе и своей работе

Озвучивая название компании, я обычно задаю вопрос: «Как вы думаете, чем мы занимаемся?». Я технический директор «Мы делаем сервисы». А еще мы работаем по собственной методологии, которую совершенствуем параллельно с развитием компании — Repeatable Software Development Process (RSDP). По факту мы специализируемся на веб-разработке: frontend и backend для корпоративных клиентов.

Как твоя работа связана с аудитом и управлением сложностью кода? — На Moscow Python Conf ++ ты будешь рассказывать, в том числе, про собственный линтер.

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

Имеет ли Python какие-то особенности с точки зрения управления сложностью кода? — Ты будешь выступать перед разработчиками именно на Python.

Конечно!

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

У него появляются новые синтаксические элементы, новые концепции и модули в стандартные библиотеки, которые ломают все, что было до этого. Во-вторых Python активно развивается.

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

Я бы даже сказал, что в плане сложности у Python все достаточно хорошо относительно других языков. Нет. Например, если использовать Webpack, появляется возможность писать функцию `import()`, которая подгружает модули асинхронно. В JavaScript действительно все плохо по одной простой причине: в коде проекта JS смешиваются сразу несколько сущностей, которые не относятся к самому языку — сторонние плагины и библиотеки, которые используются для сборки проекта. Получается, что сборщик засовывает какие-то свои внутренности в ваш язык программирования, и в итоге вообще непонятно, что происходит.

А чтобы понять, как они работают, нужно следить за стандартами языка, за конкретной реализацией и т.д. Управлять сложностью тяжело, когда язык меняется от установки Babel или плагинов к нему.

Язык развивается достаточно планомерно, и у этого развития есть понятные вехи. В Python ситуация намного лучше. И это все-таки backend, к которому мы привыкли предъявлять более высокие требования, чем к frontend. В нем нельзя кардинально поменять синтаксис двумя строчками в конфиге. Однако, по моему мнению, в Python довольно много новых изменений, которые ломают то, что было раньше, принося сомнительную пользу.

— То есть с развитием языка все становится хуже?

По факту сейчас есть два абсолютно независимых языка программирования с похожим синтаксисом: Python и Python + AsyncIO. Если вспомнить, что появился AsyncIO — по сути второй язык внутри Python — конечно, сложность возросла очень сильно. То есть Python как сущность стал сложнее ровно в два раза, потому что у него появилось два отдельных потомка, работающих по разным правилам.

Однако когда ты просишь противников этого мнения, например, запустить асинхронную функцию из синхронного кода — у них не получается. Мнение о том, что это разные языки программирования, не популярно. Хочешь использовать синхронную библиотеку для работы с БД — пожалуйста. Библиотеки тоже абсолютно разные. А хочешь асинхронную — ее нет.

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

— Влияет ли на управление сложностью то, что в программировании сейчас довольно много людей со слабой технической базой?

Для таких людей даже специальный язык программирования придумали. Конечно. Я не шучу. Называется Go. Python им не подходил по производительности, нужно было что-то иное, и в Google придумали Go. Действительно, целью создания языка Go была попытка вовлечения в написание кода студентов и стажеров Google, которые не могут освоить C++. Но какой ценой достигнута эта простота? Как оказалось, очень много людей готовы на нем писать, потому что он очень простой. Нет дженериков, нет такого понятия, как исключения и т.д. Нам дают не нормальный язык программирования, а его очень урезанную версию — в нем нет практически никаких сложных концептов by design. И поклонников такого подхода много.

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

— Какие типовые проблемы есть у чужого кода?

Обычно они разделены на две части.

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

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

— И как это выглядит на практике?

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

— В чем ты видишь главную причину того, что эти проблемы вообще существуют?

Все люди не умеют писать код.

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

Неужели это плохо? — Но ведь использование разных паттернов программирования — это по сути и есть инженерный поиск?

Но он тоже должен быть управляем. Конечно, инженерный поиск важен и нужен. Перед каждой подобной задачей необходимо выставлять четкие критерии и ограничения: по затраченному времени, по бизнес-требованиям, по инженерным практикам и инструментам.

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

Будет ли хорошо, если переписать? Практически все клиенты, которые обращаются к нам за аудитом, страдают от типичной ситуации: кто-то им что-то сделал, они наняли разработчика, чтобы как-то развить решение, но тот пришел и развел руками: «Я вообще не знаю, что здесь делать, давайте все заново перепишем». Когда ты решаешь заново переписать, наступаешь ровно на те же грабли: ты доверяешь задачу другому разработчику, который делает другие ошибки, но в итоге все получается точно так же. Не будет.

— Нужен какой-то другой подход?

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

У всех есть понимание того, что код состоит из строчек — это самая простая сущность, из которой он может состоять. Дам небольшой inside к докладу. Каждая строчка может быть написана как

x = 1,

а может быть как

x = Math.median(forecast_data) if forecast_data else compute_probability(default_model).

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

Потому что управление — это про вменяемые запреты. В итоге мы не занимаемся тем, что запрещаем делать многие вещи.

— Попадались ли тебе какие-нибудь забавные вещи в чужом коде?

У меня даже есть репозиторий, где я собираю такие примеры кода. Безусловно.

Если честно, когда я на это смотрел, у меня внутри интерпретатор сломался. Самый страшный пример, который я видел, продемонстрировал мне, что внутри цикла на сотню итераций можно определить функцию. Я догадывался, но не знал, что так можно.

Кто-то жаловался на жизнь, на работу, были и те, кто писал: «Понимаю, что пишу ерунду, но заказчик меня заставляет». Был случай, когда мы видели в коде очень много смешных комментариев. Они просят решить свою задачу, а уж какой код ты там напишешь, им вообще до лампочки. Однако заказчики обычно не заставляют тебя писать плохой код.

— Линтеры, code review — не спасают?

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

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

И я отвечаю: очень просто, в CI ставишь строчку — проверить мой код — и если она падает, все, ты внедрил. Меня, кстати, часто спрашивают: а как это внедрить? Благо, сейчас есть автоформатеры и возможность рефакторить код файл за файлом. Осталось только все отрефакторить. Следующий вопрос традиционно: как объяснить бизнесу, что это важно?

— Есть ли какой-то общий ответ на этот вопрос?

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

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

— В итоге это административная проблема?

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

То есть здесь все вопросы решаемы: нет неразрешимых противоречий.

Это не помогает быстро понять, что же хорошо? — Есть же code style — PEP 8.

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

— Не хватает какой-то общеизвестной более высокоуровневой штуки?

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

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

Это должен быть инструмент, который поставляет не только возможности, но и ограничения для разработчиков. Вообще наша недостижимая глобальная задача — автоматизировать code review, чтобы сам Python (если мы говорим про наш случай) знал, как нужно его писать.

Нельзя ли использовать (или развивать) существующие? — Зачем ты вообще разрабатываешь линтер?

Наш линтер по факту является плагином для Flake8. На самом деле мы так и делаем. Просто позиционируем его именно как полноценный инструмент, а не просто плагин.

Pylint делает очень много того, чего именно линтер делать не должен. Почему Flake8, а не Pylint? Плюс он выдает очень большое количество ошибок, которых на самом деле нет. Например, в нем реализовано очень большое количество проверок на тип, хотя типами должен заниматься type checker. Он сложен в настройке. А еще мне не нравится его документация, и меня пугает его собственная реализация ast. Поэтому у нас задача — сделать инструмент, который нельзя конфигурировать. Давая возможность конфигурации, ты позволяешь людям сделать неправильный выбор. Чтобы ты его поставил — и все.

Или здесь только твой собственный опыт? — Какие гайды легли в основу этого линтера?

Какие-то правила портировали из других линтеров: ESLint, Pylint, SonarQube, Credo. Сейчас в его основе правила, которые мы многие годы формулировали для себя на code review. Всегда оглядывались на Кошелек Миллера. Многое взяли из прекрасной работы «CognitiveComplexity». То есть на данном этапе это «сборная солянка». Отдельные правила — это мое видение, появившееся после оценки большого количества чужого кода.

— О чем ты будешь рассказывать на Moscow Python Conf ++?

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

— На кого ориентирован доклад?

Хотя и им будет интересно послушать и высказаться. Я думаю, что это все-таки сложившиеся разработчики, потому что начинающие программисты еще не сформировали своего четкого мнения. Они точно являются нашими пользователями.

Друзья, спешим напомнить, что до нашей Moscow Python Conf++ осталось меньше месяца. В этом году на ней будет более трех десятков выступлений и целая серия митапов. Финальная программа будет анонсирована на днях, а пока можно ознакомиться с общим списком докладов.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»