Казалось бы, в мире программирования давно практически ничего не меняется — процесс в целом один и тот же: вы пишете код в редакторе, просматриваете ошибки компиляции, запускаете программу на выполнение, добавляете отладочные операторы и запускаете отладчик. Однако за последние десятилетия специалисты по программной инженерии, когнитивной психологии, человеко-машинному взаимодействию и образованию провели ряд исследований, которые помогли разобраться в наиболее трудных аспектах процесса программирования. В результате были предложены новые возможности для развития сред разработки, меняющие принципы взаимодействия разработчика с кодом.
Как меняется программирование благодаря этим изысканиям? Опираясь на предложенную когнитивными психологами теорию решения задач человеком и концепцию информационных потребностей, рассмотрим процесс и средства программирования. Объясним, как благодаря исследованиям были реализованы усовершенствования, направленные на устранение типичных проблем, связанных с отладкой, навигацией по коду, обоснованием проектных решений и знакомством с новыми проектами.
Психология решения задач
Принципы решения задач человеком уже давно изучаются психологами [1], и одним из главных аспектов является метод проб и ошибок. Можно провести аналогию между решением задач и лабиринтом, в котором человек выбирает путь и возвращается назад, если выбор оказался неверным. Процесс решения обычно начинается с постановки общей задачи, которую разбивают на более мелкие цели.
Для программирования характерно множество вложенных целей — разработчики разбивают общие задачи (исправление ошибки, реализация новой функции и т. п.) на задачи более низкого уровня (например, определение того, какое начальное значение задается для того или иного поля или переменной). Таким образом, программирование — это процесс превращения запланированного изменения программы в последовательность отдельных действий. В лучшем случае такое преобразование выполняется по шаблону: разработчик сразу может спланировать действия, позволяющие достичь нужной цели. Но часто бывает и наоборот, когда имеются помехи для разбиения на подзадачи или когда понимание последствий выполняемых действий оказывается ошибочным.
Чтобы лучше понять процесс разбиения планируемого изменения на подзадачи, последние можно представить в форме вопросов. В ходе изучения информационных потребностей разработчиков выясняли, какими вопросами они задаются. Как показало исследование [2], разработчики часто сетуют на трудности с решением следующих вопросов:
- Как выполнить рефакторинг, не разрушая функциональность, с которой уже работают пользователи?
- Как поведет себя данный фрагмент кода в конкретном случае?
- Какую функцию или объект следует выбрать?
- Где именно реализована данная функциональность?
При этом задаваемые вопросы обычно касаются конкретных ситуаций поведения кода, возникающих не при всех выполнениях, а лишь в случаях, когда происходят определенные события.
Средства разработки могли бы облегчать получение ответов на наиболее частые вопросы. Например, если говорить конкретно о перечисленных вопросах, инструменты могли бы:
- определять, какие методы или параметры используются вне проекта, какие функции доступны извне;
- упростить для разработчика моделирование процесса выполнения метода, уменьшив объем предварительной настройки;
- облегчить сравнение поведения похожих методов за счет автоматизации, а также получение сведений от коллег и ранее сохраненных данных о производительности;
- упростить поиск конкретной функции за счет предоставления сведений о всех методах, использованных при ее реализации.
Исследование сложных для разработчиков ситуаций и доступных возможностей позволяет оценить степень удобства инструментов программирования. Чтобы повысить точность измерений, следует обратить внимание на то, как часто возникает конкретный вопрос и сколько времени уходит на поиск ответа, а также уточнить последствия в случаях невозможности получить ответ или при неверном ответе.
Какие вопросы средства программирования должны помогать решать в первую очередь? Наибольшие трудности, с точки зрения разработчиков, вызывают вопросы, касающиеся обоснования проектных решений, понимания принципов реализации и выполнения отладки [2]. Выяснилось, что больше всего времени программисты тратят на навигацию по коду (35%), тогда как на его чтение и редактирование уходит лишь по 20%. В определенных ситуациях, например в процессе знакомства с новым проектом, возникает целый ряд проблем иного характера [3]. В литературе по информационным потребностям, помимо перечисленных здесь типовых проблем, описан широкий круг других вопросов.
Отладка
Отладка может быть сложной по множеству причин. Из-за ошибочных предположений или неверной оценки поведения программы могут рождаться неправильные гипотезы. Путь от появления симптомов проблемы до выяснения ее первопричины может быть невероятно трудным из-за длинных и сложных потоков управления или данных. Возможны сложные в устранении дефекты, связанные с проблемами расписания или синхронизации, причем таким дефектам присущи непостоянство и трудности воспроизведения.
Как показали исследования процесса отладки, основные сложности здесь связаны с прослеживанием потока данных и управления, особенно в крупных и сложных кодовых базах. Авторы исследований характеризуют отладку как блуждание в поисках информации с целью проследить многочисленные схемы взаимных вызовов различных методов, чтобы нащупать нужный путь. Разработчики сообщают о следующих вопросах, трудно поддающихся решению:
- В каких ситуациях или пользовательских сценариях вызывается данный метод?
- По какой схеме вызовы пересекают границы процессов?
- Каков первоначальный источник конкретных данных?
- Каким образом наступило данное состояние периода выполнения?
Наблюдение за действиями разработчиков позволило объяснить эти затруднения. Разработчики задаются вопросами достижимости (причинно-следственной связи), для чего выполняется поиск операций, отвечающих определенным критериям, по потоку управления. Например, чтобы понять, почему при выполнении определенного теста поведение программы отличается от обычного, разработчик сравнивает действия в период выполнения в обоих случаях, пытаясь понять, какие именно операторы выполняются по-разному. Нередко это непросто: пытаясь ответить на единственный вопрос о достижимости, можно потратить десятки минут и при этом сбиться с пути и сделать неверные предположения, которые приведут к ошибкам.
Одно из простых усовершенствований процесса отладки — возможность обратной отладки, пошагового выполнения в обратном порядке. Это может быть полезным, например, в ситуациях, когда нужно найти исходный источник данных: при обратном выполнении проще его отследить, не понадобится устанавливать множество точек останова, снова и снова выполняя один и тот же фрагмент и включая в код отладочные команды. Такая возможность предлагалась уже давно, и на сегодня она реализована в ряде коммерческих инструментов, например в Visual Studio.
Еще более удачное решение — обеспечить для программиста возможность ввести поисковый запрос, после чего среда разработки сама находит необходимую информацию в журнале трассировки выполнения. В системе визуализации графа вызовов Reacher можно, выбрав оператор кода, запустить поиск «вверх» или «вниз», результатом которого будет перечень всех операторов, выполненных до указанного или в результате его собственного выполнения. Далее программист может выполнить простой поиск по ключевым словам, находя идентификаторы в вызовах методов или операции считывания/записи поля. Затем формируется визуализация, которая наглядно демонстрирует поток управления от просматриваемого в данный момент программистом метода к каждому из связанных с ним методов и операторов. Таким образом, повышаются эффективность и производительность программирования.
Проблемы навигации
Как уже отмечалось, 35% времени разработчиков уходит лишь на навигацию: вывод результатов поиска, переключение между косвенными зависимостями и выяснение контекста задачи. Одна из причин — рассредоточенность кода: при реализации новой функции приходится собирать сведения и редактировать код во множестве файлов. Например, более 90% изменений в проектах Mozilla и Eclipse требуют работы с несколькими файлами.
К навигации также прибегают, когда нужно разобраться с особенностями использования и поведения вызываемых методов. Современные среды разработки, например Eclipse и IntelliJ, позволяют быстро перейти к определению метода, найти вызывающие объекты и просматривать цепочки вызовов в виде древовидной структуры. Тем не менее программисты по-прежнему испытывают сложности и путаются. Даже простая задача переключения между файлами замедляет работу и может приводить к ошибкам.
Возможное решение — автоматически отображать в среде разработки контекст текущей задачи, включая относящиеся к ней методы и связи. В Mylyn — подключаемом компоненте среды Eclipse — реализован визуализатор элементов с возможностью фильтрации и отображения только тех из них, которые относятся к контексту задачи. При переключении между элементами в рамках текущего контекста не нужно запоминать местонахождение функциональности и раскрывать длинные сложные структуры пакетов в поисках нужного места. Можно просто переключаться между элементами, с которыми в последнее время происходила работа или которые представляются нужными в данный момент. Таким образом, можно тратить меньше времени на навигацию и больше — на редактирование кода.
Возможен более радикальный подход — полностью изменить принципы отображения кода в среде разработки, избавив программиста от необходимости просматривать иерархические структуры каталогов, пакетов, файлов и классов в поисках нужного фрагмента. Вместо этого можно было бы автоматически отображать только нужный код, причем необязательно в традиционной форме — в виде текстового файла. Можно выводить панель, которая вместе с методами, относящимися к текущей задаче, отображает потоки данных и управления, а также отладочную информацию. Контексты задач можно было бы сохранять, что позволило бы легко возвращаться к задаче для доработки и делиться ею с коллегами. Подобная концепция реализована в Debugger Canvas, дополнении для Visual Studio (рис. 1). Судя по откликам пользователей, такое дополнение может быть удобным при работе с длинным и сложным потоком управления, с большой незнакомой кодовой базой или при анализе последствий добавления новой зависимости.
Обоснование проектных решений
Допустим, вы изучаете участок кода и видите, что при вызове метода получения данных возвращаемое им значение игнорируется; вы пытаетесь разобраться, можно ли вообще удалить этот фрагмент или он все же для чего-то нужен. При работе с большими сложными программами подобных вопросов может возникать очень много. К числу самых сложных разработчики относят следующие:
- Почему данное действие не было реализовано по-другому?
- Этот способ задействован намеренно, случайно или это заплата на скорую руку?
- Каковы принятые правила решения данной задачи?
Причина появления таких вопросов — отсутствие или устаревание документации по нормам проектирования. Среды разработки стали бы удобнее, если бы они предоставляли сведения об обосновании проектных решений и непосредственно связывали их с кодом. Нормы проектирования при этом не пришлось бы хранить в отдельных документах. Можно, например, реализовать систему «активной документации», которая обеспечивает автоматическую проверку соблюдения правил при написании кода. Сами правила можно было бы прочитать сразу же — например, на панели справа от кода (рис. 2). При редактировании мгновенно появляется отклик: нарушенные правила подсвечиваются красным. При этом в текстовом виде отображается обоснование применения задействованного правила. Для упрощения его соблюдения можно ознакомиться с примерами кода, составленного согласно правилу. Таким образом можно ускорить разработку и повысить ее эффективность.
Знакомство с новым проектом
Вы находите интересный проект разработки программной системы записи и монтажа многодорожечных аудиотреков, в котором хотели бы принять участие. Как получить весь объем сведений, необходимых, для того чтобы внести свой вклад?
В ходе исследований в области программной инженерии изучались барьеры, с которыми разработчики сталкиваются при знакомстве с новыми инициативами в коммерческих организациях и в проектах с открытым кодом. В последнем случае трудности следующие:
- выяснение контактной информации ключевых лиц проекта и получение откликов;
- уточнение заданий и артефактов;
- понимание структуры проекта и сложного кода, настройка рабочего пространства;
- устаревшая или трудная в понимании документация;
- освоение практических принципов работы, принятых в проекте [3].
На преодоление всех этих барьеров может уйти несколько дней или больше, и, заранее зная об их существовании, разработчик может отказаться от участия.
В инструментах программирования предусматривают способы уменьшить перечисленные барьеры. В частности, готовые настройки снижают потребность загружать и настраивать новые инструменты и зависимости, проверять работоспособность системы сборки. В коммерческих инструментах проблема решается за счет размещаемых в облаке уже настроенных и готовых к работе сред программирования. Один из примеров — среда Codesandbox с онлайн-редактором кода и готовыми шаблонами проектов.
Что еще можно сделать для уменьшения барьеров адаптации? Важный вопрос: какой именно объем знаний о проекте нужен разработчику, чтобы сделать в него вклад? Разработчик должен найти участок кода, с которого можно было бы начать работу, определить используемые в нем методы и прочитать код, чтобы понять принцип его действия. Вместо этого сама среда могла бы предлагать небольшое самодостаточное задание, выполнив которое, разработчик мог бы уже за 20–30 минут сделать свой первый вклад в проект.
Эту концепцию назвали «программированием микрозадач»: разработчики решают самодостаточные микрозадачи — например, дополняют функцию парой строк или перечисляют набор тестовых сценариев в описании функции. При этом не нужно просматривать всю кодовую базу и разбираться в ней — программист работает с отдельным артефактом: функцией или тестом. Среда отслеживает состояние артефактов, определяя оставшийся объем работы и автоматически генерируя микрозадачи по мере необходимости. Таким образом, на ввод в курс дела по новому проекту и создание нового кода разработчику могут потребоваться считанные минуты. Мелкие доработки среда самостоятельно объединяет и добавляет в рабочую версию кода.
***
Программирование — это преобразование замысла в конкретные задачи, действия и код их реализации. Наблюдение за действиями разработчиков, определение вопросов, на которые программисту сложнее всего получать ответы, позволяют выявить проблемы проекта создания ПО. Современные средства программирования облегчают процесс разработки, предлагая новые, более простые способы получения ответов на трудные вопросы. Однако и сами задачи, и контекст программирования отличаются многообразием, требующим альтернативных подходов. Помимо знания возможностей средств программирования, разработчик должен уметь оценивать сложности, возникающие в конкретном контексте, чтобы выбрать адекватные инструменты.
Литература
1. H. A. Simon, The Sciences of the Artificial, 3rd ed. Cambridge, MA: MIT Press, 1996.
2. T. D. LaToza, B. A. Myers, Hard-to-answer questions about code. In Proc. Workshop Evaluation and Usability of Programming Languages and Tools (PLATEAU), 2010. P. 1–6. doi:10.1145/1937117.1937125.
3. I. Steinmacher, M. A. Graciotto Silva, M. A. Gerosa, D. F. Redmiles. A systematic literature review on the barriers faced by newcomers to open source software projects // Inf. Softw. Technol. — 2015. — Vol. 59. — P. 67–85. doi: 10.1016/j.infsof.2014.11.001.
Томас Латоза (tlatoza@gmu.edu) — доцент, Университет Джорджа Мейсона.
Thomas D. Latoza, Information Needs: Lessons for Programming Tools? IEEE Software, November/December 2020, IEEE Computer Society. All rights reserved. Reprinted with permission.