умение разбираться в чужом коде
Ужасы чужого кода: как найти смысл и не умереть
Даже самым крутым программистам трудно читать чужой код. Узнайте, почему это так страшно и как с этим справиться.
В жизни каждого разработчика наступает момент, когда нужно взяться за код, написанный другим человеком. Это может быть связано с поддержкой старого проекта, оптимизацией legacy-кода, переделыванием приложения, которое не доделал другой программист.
Хорошо, если этот код написан по архитектурному паттерну, у него есть документация, комментарии или хотя бы понятные названия переменных. Однако чаще всего приходится работать с говнокодом (это термин, если что).
В этой статье я опишу личный опыт чтения ужасного кода. Поэтому приготовьтесь сначала лицезреть моё страдание и только потом получить какие-то полезные советы.
Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.
Виновник торжества
Мне и раньше приходилось сталкиваться с плохим кодом (например, своим), но недавно попался настолько ужасный, что я решил написать об этом статью.
Для начала попробуйте понять, что делает этот цикл, зная только, что приложение работает с 3D-графикой:
Первое, что бросается в глаза, — нарушение традиций в выборе имени счётчика.
Чтобы разобраться, что тут происходит, нужно понять, что такое msh, object и gm. С последним всё предельно просто:
Теперь остаётся только понять, что такое oo. Это очень хороший вопрос, потому что oo — это глобальная переменная, объявление которой нужно ещё поискать.
Вот другой фрагмент:
Название метода не вносит никакой ясности. Не говоря уже о том, что разработчик игнорирует соглашение об идентификаторах. Он даже единого стиля не придерживается, потому что в этом же файле можно встретить метод, который называется getPointOfIntersection ().
Складывается ощущение, что разработчик не знает главного правила программирования:
«Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте».
Как с этим жить
Очевидно, что невозможно заставить всех писать чистый и понятный код. Поэтому единственный выход — научиться читать всё, что попадается под руки. Это умение можно разделить на два навыка: чтение и расшифровка (эти названия условные).
Расшифровка может пригодиться, когда нужно срочно понять, как работает код. Особенно если он настолько ужасный, что просто пытаться вникнуть в проект — глупо. Рассмотрим на примере:
Это достаточно небольшой метод, который, однако, записан таким образом, что невозможно понять, что он делает. В этом случае не стоит тратить силы, чтобы вникнуть, — лучше сразу привести его в человеческий вид.
Для начала можно расставить пробелы и переносы строк:
Сделать это можно с помощью специальных инструментов, но лучше попробовать вручную. Возможно, в процессе получится что-то понять. Попутно можно добавить комментарии:
Становится понятно, что метод проверяет коллизию двух двумерных объектов. Поэтому можно поменять названия переменных и самого метода:
Когда весь код приобретёт более-менее человеческий вид, можно будет провести рефакторинг:
Например, можно вместо координат принимать объекты:
Всё это должно помочь разобраться в коде здесь и сейчас. Если же нужно постоянно работать с чужими проектами, то есть смысл попрактиковаться в чтении. Есть несколько простых советов, которые помогут в этом нелёгком деле.
1. Пробуйте писать в разных стилях
Обычно я пишу вот так:
Даже если нужно выполнить одну инструкцию, я всё равно использую фигурные скобки. Мне кажется, так легче понять, что является частью ветвления, а что — нет.
Но иногда стоит посмотреть, как пишут другие, и попробовать так же. Например, вот так:
Если часто практиковаться, можно привыкнуть, и тогда читать такие конструкции будет проще.
2. Читайте чужой код
Попробуйте зайти в случайный репозиторий на GitHub и разобраться, как там всё устроено и как оно работает. Изучите несколько проектов, чтобы научиться понимать разные стили.
Подумайте, почему разработчик пишет так, а не иначе, как он составляет документацию и так далее. Так вы не только научитесь читать чужой код, но и, возможно, найдёте пару интересных приёмов, которые захотите использовать сами.
3. Давайте другим читать ваш код
Дайте другому разработчику посмотреть ваш проект. Сразу будьте готовы к тому, что ваш код тоже так себе. Обратная связь поможет понять, что вы делаете не так, а если повезёт, получите несколько рекомендаций, как улучшить свой код.
В процессе вы можете понаблюдать, что делают более опытные коллеги, чтобы вникнуть в ваш проект.
4. Попробуйте прочесть код, который писали давно
Скорее всего, сначала вы ничего не поймёте, а потом почувствуете сильный стыд. Это нормально и случается со всеми программистами.
Сейчас вы уже лучше, чем были когда-то давно, поэтому сможете увидеть, что делали не так. Возможно, вы заметите, что до сих пор используете те же самые приёмы, и исправите это.
5. Больше рефакторинга
Иногда код необходимо переделать, даже если он работает. Конечно, это касается в первую очередь проектов, которые нужно поддерживать. Если не прибегать к рефакторингу, то придётся постоянно держать в голове весь плохой код, чтобы не приходилось каждый раз вникать заново. Кроме того, вы вряд ли заметите баги и уязвимости, потому что весь код выглядит как одна большая уязвимость.
Заключение
Последний совет: не обманывайте себя. Вы, я и любые другие разработчики пишут ужасно. Просто потому, что так устроено программирование — приложения не могут быть безупречными и совсем понятными.
Однако это не значит, что можно окончательно забить на чистоту и читаемость кода. Просто нужно понимать, что мастерство в программировании, как и в любом другом деле, это не пункт назначения — это сам путь.
Нужно стремиться к тому, чтобы писать совершенный код, но помнить, что не нужно зацикливаться на этом. Именно так мы подходим к обучению на курсе «Профессия C#-разработчик». Студенты учатся писать чистый, поддерживаемый код с документацией, который не стыдно показать коллегам.
Профессия C#-разработчик
130 часов — и вы научитесь писать программы на языке, созданном Microsoft. Вы создадите 5 проектов для портфолио, даже если до этого никогда не программировали. После обучения — гарантированное трудоустройство.
Быстрый способ разобраться в чужом коде
Данный метод будет очень полезен в типовых конфигурациях, так как там может быть очень глубокий стек вызовов процедур, в котором новичку разобраться достаточно сложно.
Предположим, нужно разобраться, как устроен механизм печати БП 3.0.
Идем в конфигуратор, запускаем режим отладки. Кстати, некоторые процессы, например, формирование отчетов могут выполняться фоново, и для их отладки требуется настроить автоматическое подключение.
Также в типовых конфигурациях есть дополнительный параметр запуска «/РежимОтладки», который так же отключает фоновое выполнение.
После запуска режима отладки заходим в документ для формования печатной формы, но перед выполнением печати запустим механизм замера производительности.
Данный механизм позволит увидеть список выполненного кода за период работы механизма.
Далее выполняем печать и отключаем замер производительности.
Теперь нужно решить: какой ключевой оператор поможет найти требуемую процедуру, в которой формируется печатная форма. Для формирования печатной формы требуется в любом случае создать объект Табличный документ, таким образом ключевой оператор будет «Новый ТабличныйДокумент» или просто «ТабличныйДокумент». Находим нужную строку, используя ключевой оператор, устанавливаем точку останова и запускаем формирование отчета.
После остановки отладки на требуемой строке кода запускаем стек вызовов.
Выходит окно со списком вызванных процедур, по которым можно выполнять переход для изучения механизма печати.
Специальные предложения
Вчера только сделал короткую заметку, что можно применять поиск в окне замера производительности
(9) Десять лет назад я тоже начинал «расследование» от кнопочки «Сформировать». Но пару лет назад мне попалась УНФ, а там на банальную печать документа жесткая мешанина из процедур модуля менеджера этого документа, специальной печатающей именно этот документ обработки и кучи вызовов из общих модулей, часть из которых спрятана в БСПшных кишках, и часть из которых вызывается вообще в фоне. Почухал репу и на меня снизошло озарение про «Замер производительности», благодаря которому очень быстро все вызовы проанализировал.
В итоге чем выше уровень абстракции, тем легче воспринимать программу, как систему взаимодействующих сущностей. А дальше кже детали реализации конкретного объекта, которые и составляют основную сложность в программировании.
Как читать чужой код? Часть 4. Программный интерфейс. Исправление чужих доработок
Сразу напишу вопросы, которые в статье не будут рассматриваться:
1. Разбор и отладка правил конвертации
2. Отладка фоновых заданий.
3. Отладка асинхронных вызовов.
Здесь если начать описывать данный процесс, то получится статья о том, как они работают. А смысла писать давно описанное нет. Для меня пп. 2 и 3 это обычный код, который разбирается в других кейсах.
Кейс 6. Как разобратьcя и использовать программный интерфейс.
1. Как понять, какой интерфейс нам предоставляет типовая конфигурация?
2. Что такое БСП (кратко)?
3. Как понять, что делает та или иная процедура/функция, какие параметры нужно передать, чтоб она сработала?
4. Чем отличается программный интерфейс от служебного? Можно ли использовать служебные экспортные процедуры/функции?
5. Он же изменится. Зачем его использовать, если 1С перепишет его?
Раскроем каждый пункт подробно:
1. Как понять, какой интерфейс нам предоставляет типовая конфигурация?
На самом деле элементарно! Давайте рассмотрим на примере ЗУПа. В ЗУП есть часто используемые разделы:
— Учет страховых взносов
Остановимся на таком списке. Конечно, разделов гораздо больше. На что надо обратить внимание: На то, что каждый раздел имеет свои Общие модули, и они называются так же, как и раздел учета. Т.е. в любой конфигурации названия общих модулей сделаны очень понятными и соответствуют разделам учета/решаемым задачам.
Здесь необходимо напомнить о стандартах разработки! Согласно стандартов по оформлению общих модулей, секция «Программный интерфейс» должна всегда располагаться вверху общего модуля. Далее следует секция «Служебный программный интерфейс».
Берем штатное расписание. Заходим в основной серверный модуль «УправлениеШтатнымРасписанием». Видим процедуры:
если посмотреть в служебный программный интерфейс, можем найти там довольно полезную функцию:
полезные процедуры можно найти даже в секции «Служебные процедуры и функции». Например, в этом модуле много функций, для построения запросов по штатному расписанию:
или процедуры, которые позволяют получать какие-то данные по позициям штатного расписания:
Окунувшись в стандарты разработки чуть глубже, станет понятно, что программный интерфейс есть не только в общих модулях. Эта область в коде может присутствовать в модуле менеджера или модуле объекта абсолютно любого объекта метаданных.
Опять же на примере ЗУП обратимся к модулю объекта обработки «Менеджер данных учета времени сотрудников». Здесь есть вот такие полезные процедуры:
Если посмотреть модуль менеджера документа «Больничный лист», можно найти функцию:
На этом перестану переписывать названия процедур и функций в текст статьи. Главное понять принцип, что общие модули, модули объекта и менеджера могут содержать программный интерфейс, который Вам требуется.
Настоятельно рекомендую изучать программный интерфейс того раздела учета, в котором Вам предстоит вести разработку по Вашей задаче. Наверняка много полезного найдёте для себя.
Что важно отметить в этом пункте: Если Вам нужно что-то сделать с таблицей, массивом, файлом, хранилищем значений или ещё с каким-то универсальным механизмом, которым «оборудованы» все конфигурации. ОБЯЗАТЕЛЬНО смотрите в описание БСП. Наверняка Ваша задача уже решена. Осталось с ней разобраться ниже описанными способами, и использовать программный интерфейс в своём коде.
С точки зрения качества кода при использовании как программного интерфейса как типового (имею в виду учетные задачи), так и БСП, Вы шагнёте на новую высоту (это больше, чем на ступень вверх!). На собеседованиях Вам наверняка зададут вопрос о том, знаете ли Вы, что такое БСП, используете программный интерфейс в своём коде или нет?
По себе могу сказать, что половину своих задач решаю именно с помощью программного интерфейса! Лучше я потрачу время на изучение этой темы, чем буду писать никому ненужный код с так себе качеством.
3. Как понять, что делает та или иная процедура/функция, какие параметры нужно передать, чтоб она сработала?
Опишу способы которыми пользуюсь сам:
— Программный интерфейс БСП описан на сайте ИТС и в большом количестве статей. Нужно не полениться воспользоваться поиском и почитать статьи.
а. Поставить точку останова в месте вызова программного интерфейса в типовом коде. Конечно, необходимо найти хотя бы одно место, откуда он вызывается. Т.е. какое пользовательское действие приводит к запуску программного интерфейса. Зачастую таких мест много и найти их нетрудно, часто даже можно догадаться.
б. Скопировать весь текст вызова программного интерфейса в свой код, и постепенно адаптировать свой код под параметры интерфейса, чтобы он сработал без ошибок. Данный путь, во-первых, сложней, во-вторых, Вам его всё равно придётся пройти, т.к. адаптация своего кода под программный интерфейс неизбежна, если Вы его хотите использовать. Поэтому обязательно идём по этому пути.
— В отладчике Вам необходимо посмотреть параметры на вход, тип значения, чем заполнены. Часто необходимо, чтобы в Менеджере временных таблиц уже была временная таблица с определёнными колонками, которая станет «фильтром» для получаемых данных. Отладчик позволяет увидеть, какие данные в этой временной таблице.
— Последним действием необходимо изучить все временные таблицы, которые создал программный интерфейс, и посмотреть на результат его работы. В моей практике нередко бывает, что использую не результирующую таблицу, а несколько промежуточных! Поэтому и надо посмотреть их все. Вдруг наилучшим образом под Вашу задачу подойдёт промежуточная таблица!?
— После того, как программный интерфейс сработал, не забывайте удалять из менеджера временных таблиц ненужные данные. Особенно если какие-то временные таблицы огромных размеров, т.е. >= 100 тыс. записей.
4. Чем отличается программный интерфейс от служебного? Можно ли использовать служебные экспортные процедуры/функции?
Для ответа на этот вопрос нужно обратиться к стандарту разработки под названием «Структура модуля». В нём указано вот какое описание:
а. Раздел «Программный интерфейс» содержит экспортные процедуры/функции, которые можно вызывать из любых объектов метаданных, в т.ч. через внешнее соединение. Т.е. не важно какую задачу решаете, если есть потребность в использовании процедур/функций этого раздела смело пользуйтесь!
б. Раздел «Служебный программный интерфейс» содержит экспортные процедуры/функции, которые являются частью какой-то подсистемы. Их использование, согласно стандарту, допускается только внутри подсистемы, в которую входит общий модуль или объект конфигурации. Т.е. если мы смотрим служебный программный интерфейс штатного расписания, то эти процедуры не следует использовать в расчете налогов или зарплаты) Да и вряд ли они туда подойдут!
в. Раздел «Служебные процедуры и функции» содержит процедуры/функции, которые используются для реализации программного интерфейса (в т.ч. служебного) данного модуля. Не рекомендуется их использование за рамками модуля, а тем более за рамками подсистемы. Но «не рекомендуется» не означает, что в модуле не могут быть экспортные процедуры/функции. Их необходимо также использовать в рамках подсистемы, в которую входит общий модуль.
Как видно, знание стандартов разработки помогает лучше понимать подходы и принципы, которыми руководствуются разработчики типовых конфигураций и тиражных решений.
5. Он же изменится. Зачем его использовать, если 1С перепишет его?
Помните, мы раньше ТиС 7.7 дорабатывали? А как ЗУП 2.5 или УПП 1.3 в хлам переписывали? Никто ведь не думал в тот момент, что 1С придумает УТ11, ЗУП3, ЕРП2? Всё меняется со временем, поэтому не стоит рассчитывать, что программный интерфейс 1С никогда не поменяет! Но вопрос тут, конечно, не в психологии, а в том, что делать, если программный интерфейс перестал работать!?
Необходимо попробовать следующие действия:
— Чаще всего меняется состав, назначение или тип значения у переменных, передаваемых в программный интерфейс. Как это понять? Найти место, где этот программный интерфейс используется в типовой конфигурации, и изучить обновлённый состав параметров.
— Необходимо зайти в модуль, в котором располагается программный интерфейс, и изучить описание самого интерфейса и параметров. Также рекомендую остановиться в общем модуле отладчиком и посмотреть, как теперь он работает, какие данные в нём.
— Иногда меняются временные таблицы, которые создает программный интерфейс, или убираются/добавляются колонки в основной таблице. Опять же увидеть можно все эти изменения в отладчике. По сути надо повторить путь разбора программного интерфейса, описанный выше.
Кейс 7. Помочь коллеге, переделать (исправить) быстро чужую работу.
1. Техническое задание
3. Консультанта, который выяснял детали и писал какую-то документацию по этой доработке.
5. Пообщаться с архитектором (если такая опция есть в проекте). Необходимо выяснить максимум того, что не так в выполненной кем-то работе и каковы ожидания.
6. Всё, что удалось выяснить, необходимо ОБЯЗАТЕЛЬНО ЗАПИСАТЬ! Не надо надеяться на свою память. Она Вас точно подведёт!
Как только выяснили постановку, сценарии, ошибки и как должно быть. Срочно начинаем доработку. Какие действия Вам помогут ускориться:
1. Попробуйте выяснить у архитектора правильный путь решения задачи. Как бы он сам делал? Может, даже подскажут, в какие модули надо залезть, и пояснят подход к решению задачи.
2. Если реализация уже есть хоть какая-то. Попробуйте совместно с консультантом/аналитиком протестировать доработку. Иногда достаточно поправить несколько мелких ошибок, и ситуация спасена!
3. Если в процессе кодирования столкнулись с непониманием кода. Уже в этой статье много способов в нём разобраться описано. Не стесняйтесь просить более опытных коллег пояснить кусок кода, который непонятен. Не нужно полдня тратить на разбор 10 строк кода.
Остальные части доступны по ссылкам:
Добавляйте себе в избранное, ставьте «+». Статья разрабатывалась 1,5 месяца, а опыт копился 18 лет!
Также напомню о своих предыдущих публикациях, в особенности про статью об архитекторе. Текст статьи полностью переработан, сглажены углы. Будет интересно, даже если уже читали. Вот ссылки:
8 советов для быстрого понимания чужого кода
На Quora матёрые программисты дали тонну советов и техник, которые помогут сориентироваться в базе кода, написанного не вами и не вашей командой.
Такое часто случается с проектами, доставшимися «по наследству» от предыдущих разработчиков. Вам уже нужно писать новые блоки, а в старой базе over 9 000 строк кода. Как быстро освоиться и не утонуть юному падавану?
Самый очевидный совет — найти программиста, который уже знаком с этим кодом, и слёзно попросить его отвечать на все ваши глупые вопросы в течение пары следующих недель. Ещё лучше поговорить с тимлидом проекта и заранее попросить его снизить нагрузку на программиста, который любезно согласится побыть вашим наставником.
Отсюда две линии развития событий:
— Вы нашли себе учителя (если вы работаете в команде)
— Вы не нашли себе учителя (такое может случиться, если вы работаете над опенсорсным проектом)
I. Вариант с менторством. Как облегчить жизнь себе и вашему кунг-фу гуру?
II. Вариант без менторства. Как разобраться во всём самостоятельно и ничего не сломать? (программа на 3 недели)
Так или иначе, в обоих вариантах новичку (даже если вы не новичок в программировании, вы можете быть новичком в этом коде) нужно задать себе четыре глобальных вопроса:
— Что делает этот код?
— Как он это делает?
— Что нужно сделать, чтобы он стал лучше?
— В каком месте это нужно сделать?
Ответы на эти вопросы находятся в самом коде. Если код уже выполнен аккуратно и разбит на чёткие модули и объекты, проблем у вас не возникнет. Пользуйтесь баг-трекером, отладчиком, читайте документацию и не стесняйтесь приступить к исправлению найденных багов.
Sourcetrail: инструмент, чтобы разобраться в чужом коде и не выстрелить себе в голову
I regret to report that I’ve just recently looked again at my programs for prime factors and tic-tac-toe, and they are entirely free of any sort of comments or documentation.
— Donald E. Knuth
Каждый программист не раз сталкивался с ситуацией, когда приходится читать чужой код не имея представления для чего нужна та или иная функция, класс и как оно вообще все взаимосвязано.
Такое случается даже со своими программами и скриптами, написанными на write-only ЯП.
Разработчики, имеющие дар работать с таким кодом высоко ценятся в коллективе.
Такое чудо-лабиринты из кода бывают, когда исходный код имеет:
Герменевтика исходного кода требует особых навыков и инструментов. Навыки такого рода приходят лишь с опытом, однако доскональное знание синтаксических особенностей языка и сопутствующей документации могут облегчить дело тем, кому не хватает практического опыта. Другим таким подспорьем являются специальные инструменты исследования исходного кода.
Sourcetrail на помощь
Приложение Sourcetrail представляет собой продвинутый интерактивный проводники и анализатор, который позволяет активно исследовать исходный код, индексируя и собирая данные о его структуре. Интерфейс приложения включает в себя три основные части.
Структуры и классы отображаются курсивом; пакеты, модули и пространства имен — розоватым цветом; функции и методы — желтым цветом, а переменные и области — голубым. В данный момент реализована поддержка следующих языков программирования:
Мы выбрали проект tictactoe_cpp и затем метод Run. С помощью графа наглядно можно увидеть, что Run вызывается единожды из функции main. Помимо вершин графа, также и ребра несут полезную смысловую нагрузку.
Кроме того существуют определения для переопределения (слева) и наследования метода (справа).
Sourcetrail всячески стремится реализовать интерактивное поведение. При выборе объектов графа в представлении кода автоматически подсвечиваются соответствующие символы. Обратное также верно, выбор символов в представлении кода приводит к автоматической перенастройке графа. Выбранный на графе элемент сразу же переходит в центр, также обновляется содержимое в панели кода. При наведении курсора на объекты графа соответствующие символы выделяются прямоугольной рамкой. И снова обратное верно для наведения курсора на символы в панели кода.
Можно задавать уровень детализации и глубины построения графа с помощью trail controls в левом верхнем углу.
Интересной, для подобного класса приложений, концепцией пользовательского интерфейса является навигация в стиле веб-браузера. На Рис. 2 видны кнопки Назад, Вперед и Дом. Надо сказать — действительно удобно. Кнопка Обновить не перерисовывает граф, как можно было бы предположить, а предлагает заново индексировать файлы исходного кода. Кнопка Закладки позволяет добавить символ в избранные.
Одним из инструментов trail controls является custom trail, в самом верху. Вызвав соответствующую форму, можно выбрать необходимые составные графа, детализацию, отправной и целевой узел и тип построения.
Установка Sourcetrail
Дистрибутив приложения Sourcetrail доступен для ОС Windows, macOS и Linux. Пользователи Windows как обычно запускают setup.exe, для macOS установка проходит штатными средствами операционной системы. Для Linux существует два варианта:
/.config/sourcetrail.
Текущая версия Sourcetrail требует установки среды выполнения Java 8 для индексации Java проектов. Бинарная архитектура Sourcetrail, должна соответствовать таковой для JRE. Для Sourcetrail 32-бит нужна 32-бит JRE, а для 64-бит версии приложения — соответственно 64-бит JRE.
Поддержка сторонних IDE и редакторов
Sourcetrail поддерживает интеграцию с целым рядом IDE и текстовых редакторов. Все плагины идут с открытым исходным кодом, разработка ведется на github.
Возможности CLI
С помощью командной строки можно индексировать, или настроить проект в Sourcetrail.
Резюме
Бытует мнение, что разработчики тратят большую часть времени на то, чтобы осмыслить существующий код, нежели на написание нового. Такие средства анализа исходного кода, как Sourcetrail могут существенно сместить баланс в пользу последнего, без потери качества. Хочется отметить фокус на продуманный и знакомый динамичный пользовательский интерфейс, который отлично выполняет роль ненавязчивого помощника и существенно экономит время на рутинных процедурах.