Pull to refresh

Работая в интересах Будущих Разработчиков

Reading time 11 min
Views 16K
Original author: John Pignata
Успешный программный продукт обычно проходит за свою жизнь через руки множества разработчиков. Вы — лишь одно из звеньев в цепочке опекунов вашего проекта, и каждая строчка кода, которую Вы написали — это оставленный Вами артефакт, который когда-нибудь будет изучаться Будущим Разработчиком. Также, как Вы унаследовали решения разработчиков, которые были до Вас, другие разработчики унаследуют решения, которые Вы приняли сегодня. Они получают от нас в наследство все наши недоразумения, срезанные нами углы, примененные нами недопонятые паттерны и техники, наше невнимание к деталям, нашу лень, наши изменения, сделанные на скорую руку, наших скелетов в шкафах, наше грязное белье. И гораздо реже — выгоду от нашей дисциплинированности, наших обсуждений и подготовок.



Как разработчик своего проекта, Вы находитесь в лучшей из возможных позиций чтобы предвидеть и позаботиться о нуждах Будущего Разработчика. Каждое хорошее решение, которое мы принимаем в нашем проекте, для его продуктивности будет иметь волновой эффект. Почему это важно? Как Боб Мартин спрашивает в книге Clean Code: «Вы когда-нибудь испытывали проблемы из-за плохого кода? Тогда почему Вы пишете его?». Те же стратегии, что улучшат условия поколениям команд, которые будут работать над вашим проектом в будущем, будут служить добром вашей команде и в настоящем. Когда Вы возвращаетесь в темные уголки кода, налепленного Вами полгода назад, Вы знаете о нем чуточку больше, чем будет знать Будущий Разработчик, когда увидит его в первый раз. От подсказок и глянца, которые Вы оставите другим разработчикам, в будущем выиграете и Вы сами. Проекты которые находятся в плохом состоянии, и в которые сложно внести вклад, приводят к истощению команды. Инвестиции в качество и в простоту поддержки в будущем продукта, который Вы создаете — это инвестиции в счастливое, продуктивное рабочее место для будущего и настоящего.

Я хочу разобрать несколько практик (без какого-либо определенного порядка), которые мы можем использовать, чтобы направить Будущего Разработчика прямиком к успеху.

1. Целостный рефакторинг


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

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

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

Внутри Ruby on Rails обосновалось много мощных взглядов и соглашений. Они позволяют разработчикам подключаться к проекту и сразу же становиться продуктивными, при том условии, что они имели опыт общения с этим фреймворком ранее. Иногда мы относимся пренебрежительно к этим соглашениям, тем самым ослабляя их силу. К примеру, в рельсовых проектах периодически можно заметить контроллеры, построенные в нескольких разных стилях. Одни составлены с помощью чего-нибудь вроде resource_controller, другие следуют стандартному соглашению о работе с ресурсами, принятому в Rails, тогда как третьи просто являются свалкой мусора из случайных экшенов. Другой частовстречающийся анти-паттерн состоит в рассыпаных по всему проекту и инициализирующихся везде, где возможно, конфигурационных данных.

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

Добавьте соглашения в Ваш README или в репозиторий документации. На первых порах это упростит Будущему Разработчику добавление нового функционала в систему и даст понимание того, как построены ее компоненты.

2. Искореняйте Мертвый Код


Другая общая черта систем, которые уже успели просуществовать некоторое время, это слой мха в форме мертвого кода. Это компоненты Вашего проекта, которые могли какое-то время назад представлять важность для бизнеса, но устарели и долгое время никому не были нужны. Скорее всего в проекте можно найти большое количество full-stack acceptance тестов, проверяющих такие части функционала, и сейчас эти тесты лишь замедляют прохождение остальных.

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

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

Удалите код, который давно заброшен. В конце концов, он все еще находится под контролем версий, на случай, если к нему нужно будет позже вернуться. Не обманывайте себя тем, что нужно будет «снова вернуться к нему позже». Если этот код имел хоть какое-нибудь значение, то почему он уже не используется?

3. Оставляйте за собой информацию


Помимо, собственно, нашего проекта, некоторые инструменты, которые мы используем для написания кода, тоже имеют свои артефакты. Например, существует много принятых практик о том, что составляет гигиену хорошего сообщения при git commit, и при этом проекты все равно продолжают собирать такие истории коммитов:
jp@oeuf:~/workspace/blog(master*)$ git log --oneline app/controllers/application_controller.rb
8ec7f99 fuck i dunno lol
ffa919a shut up, atom parser
a33e9fa fixing again
cecc9dc one more time
968a28f fixing
3e3aeb2 ws
1fc597e pagination
edea155 adding dumb feature

На тот случай, когда Будущий Разработчик неизбежно придет к использованию git blame, чтобы понять контекст данной фичи, оставьте ему подробности, которые помогут понять происходящее в файлах на самом деле. Используйте --squash, commit --amend, rebase и прочие инструменты, чтобы привести коммиты в годное состояние прежде чем интегрировать свою ветку. Перефразируйте свои коммиты, когда закончите — найдите минуту, чтобы включить все, что кажется подходящим и подводит итог проделанной работы. Откорректируйте грамматику и правописание; Вы публикуете нечто такое, что придется читать и понимать другому человеку. Окажите Будущему Разработчику услугу и убедитесь в том, что Вы оставляете за собой понятную запись, содержащую подробностей ровно столько, сколько необходимо:
jp@oeuf:~/workspace/blog(master*)$ git log app/controllers/application_controller.rb
commit 8ec7f998fb74a80886ece47f0a51bd03b0460c7a
Author: John Pignata <john@pignata.com>
Date:   Sat Nov 3 14:11:12 2012 -0400

    Add Google Analytics helper

commit 968a28f366e959081307e65253118a65301466f2
Author: John Pignata <john@pignata.com>
Date:   Sat Nov 3 13:49:50 2012 -0400

    Correct ATOM feed validation issues

    Using the W3C Validator (http://validator.w3.org/appc/), a few trivial
    errors were reported:

    * <author> should have a <name> node within it and not text
    * Timestamps should be in ISO8601 format

    This change fixes these issues and improves the spec coverage for the XML
    document.

commit 3e3aeb27ea99ecd612c436814c5a2b0dab69c2c3
Author: John Pignata <john@pignata.com>
Date:   Sat Nov 3 13:46:24 2012 -0400

    Fixing whitespace

    We're no longer indenting methods after `private` or `protected` directives
    as a matter of style. This commit fixes whitespace in all remaining
    classes.

commit 1fc597e788442e8cc774c6d11e7ac5e77b6c6e14
Author: John Pignata <john@pignata.com>
Date:   Sat Nov 3 12:34:50 2012 -0400

    Implement Kaminari pagination

    Move from will_paginate to kaminari in all controllers. The
    motivation is to be able to paginate simple Array collections
    without the monkey patching that will_paginate uses.

    * Consolidate helpers
    * Clean up whitespace

commit edea15560595bab044143149a7d6e528e8ae65d2
Author: John Pignata <john@pignata.com>
Date:   Sat Nov 3 12:27:16 2012 -0400

    Add ATOM feed for RSS readers

    * Include Nokogiri in Gemfile for its builder
    * Add AtomFieldBuilder model
    * Add link to feed from index page

4. Наведите глянец на свои интерфейсы


Некоторые Рубисты избегают видимые методы в своих объектах. В чем цель? Любой метод на самом деле вызывается через send, как ни крути. Зачем заковывать в кандалы некоторые из них? Просто добавьте метод до кучи к остальным и если Будущий Разработчик захочет использовать его, то он сможет! Мы ведь все взрослые, не так ли?

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

Прячьте как можно больше внутренностей компонентов, чтобы содержать интерфейс маленьким и сосредоточенным. Приложите дополнительные усилия, чтобы убедиться в том, что публичные интерфейсы Вашего объекта очевидны, целостны и имеют хорошие названия. Это даст Будущему Разработчику понятные сигналы о том, как Вы задумывали использовать каждый из объектов и подчеркнет то, как каждый из них может быть использован повторно. Явно указывайте видимость методов, чтобы передать эти предпосылки и соблюсти четкие границы публичного интерфейса.

5. Оставляйте комментарии, не слишком много, в основном RDoc


Наши чувства, как разработчиков, к комментариям в коде наиболее точно могут быть описаны как противоречивые. С одной стороны комментарии очень помогают читателю в понимании того, как работает определенный кусок кода. С другой стороны ничего не гарантирует их корректность, комментарии к коду — это ложь, которая ждет своего часа. Если спросить разработчиков, то они ответят, что ценят и почитают документацию, но большинство проектов на деле имеют небольшой, почти-потерявший-актуальность README файл и могилку wiki где-нибудь рядом. Более того, работая с open source библиотеками, мы частенько ожидаем подробную RDoc документацию, свежий README файл и хорошие примеры кода, а когда не обнаруживаем ничего из этого, грязно ругаемся. Чертов разработчик: не поддерживает документацию, наверное ждет, что кто-то сделает это за него.

Чем больше мы уделяем внимания вещам вроде Single Responsibility Principle и используем паттерны слабой связности между объектами, мы начинаем видеть системы составленные из множества маленьких объектов, объединенных вместе на время работы программы. Хотя это и делает системы более гибкими, а объекты становится проще использовать многократно, присутствует компромисс: понять место объекта внутри большой системы становится сложнее, это требует больше усилий и времени. Можно применить все привычные приемы рефакторинга, чтобы удалить надоедливые междустрочные комментарии и сделать объект настолько читабельным, насколько это возможно, но то, как объект должен взаимодействовать в системе, все еще может сбить с толку Будущего Разработчика.

Документация в стиле RDoc может быть найдена во многих open source проектах. Когда Вы пользуетесь Google, чтобы понять, вызывает update_attribute коллбэки или нет, и какой сигнатурой обладает select_tag, Вы обычно попадаете на страницу RDoc для Ruby On Rails. Написание аналогичной документации для Вашего проекта даст Будущему Разработчику понять больше, когда он будет пытаться определить роль объекта в огромном контексте твоей системы. Наличие короткого, декларативного предложения наверху класса и/или метода, показывающего, за что он отвечает, может иметь существенное значение для читающего код. Но без хорошо распространенной культуры поддержки актуальности этих комментариев, они также могут оказать и негативное влияние, дизориентировав читателя кода. Помните, единственное, что может быть хуже отсутствия документации — это плохая документация.

6. Пишите тесты, раскрывающие Ваши замыслы


Один из способов снабжения проекта документацией — это тесты. Тесты не только описывают поведение компонентов, но и гарантируют, что такая документация — качественная, т.к. она исполняется. В отличие от комментариев, мы не можем наврать в тестах. Они либо зеленые, либо нет. Такие инструменты, как RSpec и minitest/spec помогают нам генерировать эту побочную документацию, поощряя прозу внутри блока тестового примера. К сожалению, иногда мы смотрим мимо Английских слов, пытаясь добиться работающего кода во время цикла red-green-refactor. В результате пренебрежения Англоязычным описанием, иногда получается так, что тесты отображают поведение наших объектов не так хорошо, как мы думаем.

Почти так же болезненно, как найти проект без тестов вообще, найти проект, чьи тесты не помогают понять, как система работает. Тесты это код, который также требует поддержки, а поэтому они должны очень четко пояснять свое предназначение будущему их читателю.
it "works" do
  data = File.open("fixtures/projects.txt").read
  index = ProjectIndex.new(data)
  index.should have(40).projects

  last_project = projects.last
  last_project.title.should eq("ORCA")
  last_project.customer.should eq("Romney-Ryan 2012")
  last_project.deploy_date.should eq(Date.parse("2012-11-06"))
end

Хорошо, что здесь «работает»? Это описание в одно слово бессмысленно, и в примере происходит сразу несколько проверок, что совсем не оставляет за собой никакого смысла.

Если Вы пишите тесты в стиле спецификаций, Англоязычные описания должны ходить у Вас по струнке. Один из способов добиться этого, это запускать RSpec в формате документации:
jp@oeuf:~/workspace/project-manager(master)$ be rspec --format documentation spec
ProjectIndex
  .new
    instantiates an index given the contents of a project CSV file
  #projects
    returns a collection of projects from the index
Project
  #title
    returns the project title
  #customer
    returns the Customer record for the project
  #deploy_date
    calculates the deploy date from the latest project status

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

Будущий Разработчик в восторге


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

Спасибо Dave Yeu у которого я позаимствовал (читай: украл) термин “будущий разработчик”.

Есть мысли, вопросы или комментарии? Делитесь ими, пожалуйста! Меня можно найти в Твиттере под ником @jpignata, а также я доступен по электронной почте john@pignata.com. Спасибо за чтение!

Напомню Вам, что это перевод, а координаты выше — относятся к автору оригинальной статьи. Я буду очень благодарен, если Вы напишите мне свои комментарии и замечания в личку. Спасибо.
Tags:
Hubs:
+33
Comments 17
Comments Comments 17

Articles