[Перевод] Как решить любую программерскую задачу

Привет всем!

Сегодня вашему вниманию предлагается перевод по-своему незаменимой статьи, которая поможет вам правильно подойти даже к самому коварному и нетривиальному ТЗ, которого вы на первый взгляд в упор не понимаете. Главное — не сдаваться и толково формулировать вопросы. Господин Джастин Фуллер из «Бэнк оф Америка» любезно излагает, как это правильно делается.

-cefmbih1m5grvz7l0wlcwygxug.jpeg

Приятного чтения!
Итак, этот день настал. Может быть, это ваш первый рабочий день, а может быть, вы трудитесь на этом месте уже десять лет — не важно. С каждым такое бывает: вам наконец-то попалась задача, которую вы просто не понимаете.

Что же делать? Просто приступать и надеяться, что все заработает само собой? Либо признаться начальнику, что вот этого сделать не сможете, так как не понимаете?

Думаю, вы догадались, что правильный ответ — ни то, ни другое!

Я заметил, что в программировании, как и в любой другой профессии, практически невозможно прожить рабочую неделю (а иногда и рабочий день) не столкнувшись с совершенно непонятной задачей.

Но не переживайте! Есть отличная новость. Мало того, что вам под силу решить эту проблему — вдобавок, она еще и может пойти вам на пользу.

Ведь при этом вам придется расширить свои знания и навыки по сравнению со всем, что вы делали и знали ранее.

Далее я расскажу, как перекинуть мостик между требованиями, которые перед вами поставили, и знаниями, необходимыми вам для выполнения поставленной задачи.

О «требованиях»


Как вы уже заметили, я употребляю слово «требования». В зависимости от того, где именно вы работаете, это слово может обладать теми или иными коннотациями.

По моему опыту, в больших компаниях требования любят, а в маленьких иногда «не выдвигают требований». Думаю, именно об этом нам здесь и стоит поговорить.

Дело в том, что, в конечном счете, все программисты делают одно и то же: решают задачи.
Можно получить исчерпывающее стостраничное ТЗ о том, как решить эту задачу (однажды я побывал на часовом совещании по поводу текста на кнопке). А может быть и так: CEO идет мимо вашего стола и как бы ненароком спрашивает — «а к пятнице с этой задачей справитесь?»
В обоих случаях —  перед вами поставлена задача, и вы должны быть уверены, что полностью ее понимаете; только так вы сможете правильно к ней подступиться!

Об этапах


b3iohptc9o-9zddfgctacswc9ai.jpeg

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

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

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

Однако, это нормально. Это означает, что вы соберете разрозненные знания и соберете их в единое целое — и так сможете добиться наилучшего возможного результата!

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

Этап первый: анализируем задачу

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

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

Классифицируем задачу

Классифицировать задачу — означает определить, какую работу придется выполнить для ее решения. Вот несколько примеров типов задач:

  • Исправление бага
  • Новая фича
  • Новое приложение
  • Исследовательское задание
  • Оптимизация производительности


Помните, что этим список вариантов не исчерпывается.

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

Данный этап особенно важен при нечетких требованиях, например: «Нужен способ как-то очищать наши клиентские кэши после обновления сайта».

Вот несколько возможных интерпретаций этого требования.

  1. Вы должны оперативно реализовать некий механизм очистки кэша, такой, чтобы клиенты всегда получали самые актуальные обновления.
  2. Необходимо изучить все способы хранения этих клиентских кэшей и определить наилучший (е) вариант (ы) избавления от этих кэшей после каждого обновления сайта.
  3. Клиентские кэши уже должны были быть очищены, и от вас требуется найти и пофиксить баг, который этому мешает.


На данном этапе, если вы не абсолютно уверены, какая трактовка имелась в виду — нужно обратиться за разъяснениями, и лишь потом продолжать работу.

Сформулируйте суть задачи в виде одного-двух простых высказываний.

Резюмируйте сложное требование, словно отвечая на вопрос «над чем вы сегодня работаете?» Может быть, это будет не так просто, но вы должны постараться и свести суть задачи к одному-двум предложениям.

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

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

А вот плохой пример: «Обновляя сайт, мы прикрепляем к файлам уникальный номер, так что браузер понимает, что должен использовать новейшую версию кода. Также мы должны послать сообщение в нашу сеть CDN, уведомляя ее таким образом о необходимости обновить файлы. Еще потребуется предусмотреть, чтобы приложения под IOS и Android отправили обновление на рынок приложений. Еще…»

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

Выделить важнейшие детали

Теперь вы должны (в свободной форме, наиболее удобной для вас) составить список основных вещей, которые необходимо сделать.

Это, опять же, должны быть очень простые выжимки каждого из основных этапов.

Речь не идет о пошаговых или подробных руководствах по устранению проблемы.

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

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

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

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

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


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

  • Пользователи должны иметь возможность сообщить нам, что больше не хотят видеть определенные рекламки.


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

Прежде чем двинуться дальше, хотелось бы ответить на вашу возможную критику.

Возможно, вы полагаете: «В нормально поставленном бизнесе работа такого рода должна делаться еще до того, как требования лягут на стол к разработчику» — и я с вами решительно соглашусь!

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

Определите проблему (проблемы), которые пытаетесь решить.

Ответьте на вопрос: «зачем кому-либо придется это использовать?» или «какова реальная или ощущаемая проблема, которую я в данном случае пытаюсь исправить?»

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

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

Этап второй: интерпретируем и оцениваем требования

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

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

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

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

Возможно, вы и так понимаете, что нужно изобрести способ обращаться к агрегированной пользовательской информации, но понимаете ли вы, что означает «добавить ее в dao»?
Вероятно, вы понимаете, что должны форматировать рекламные данные, но представляете ли вы, что такое «MADF» (разметка рекламной новостной ленты)?

Я — ни того ни другого не понимаю.

Вот почему нужно вычленить и определить все важные термины. Запутаться в определениях — верный способ решить задачу некорректно.

Определитесь, как должна быть сделана задача

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

В некоторых командах никто не будет вам объяснять, как именно реализовать требования, просто скажут, какой функционал должен получиться на выходе.

В других будет подробно расписан каждый ваш шаг.

Скорее всего, вам доведется оказаться в некой промежуточной ситуации.

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

Этот шаг кажется совершенно естественным, но важно обращать внимание именно на то, в каком порядке мы к нему приступаем.

Естественно, нам хочется окунуться сразу во все подробности задачи и изучать их до тех пор, пока поставленная цель не станет нам совершенно понятна.

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

Определите, решаются ли поставленные задачи

На данном этапе стадии анализа и интерпретации сливаются воедино. На этапе анализа вы сосредотачиваетесь на целостной картине и крупномасштабных целях — что мы делаем и зачем.

На этапе интерпретации сосредотачиваетесь на деталях — как мы это делаем.

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

Спросите себя: приведут ли шаги, которые мне было поручено сделать, к результату, который обозначался в качестве конечной цели поставленной задачи? На самом ли деле запланированный результат решает изначальную проблему?

Если вы уверены, что все проблемы решаемы, а детали осмысленны, то можно приступать к работе! В противном случае необходимо перейти к третьему этапу, чтобы разрулить всяческие конфликты.

Этап третий: подходим к проблеме критически

На данном этапе вы должны быть в состоянии уверенно утверждать, что понимаете и задачу, и решение. Остается убедиться лишь в том, правильное ли это решение.

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

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

Итак, изложим основные правила грамотного несогласия

Знать, когда не соглашаться

  • Несогласие недопустимо, пока не разобрался в проблеме досконально.
    Говорить «это неверно» можно лишь при абсолютной уверенности, что вы понимаете, с чем именно не соглашаетесь/
    Если вы не можете уверенно сформулировать проблему и планируемое решение, то не соглашаться нельзя. Если вы не убедились, что все верно понимаете — не соглашаться нельзя. Лишь будучи уверенным, что разбираетесь в проблеме до мелочей, можно позволить себе несогласие.
    Если чувствуете, что не обладаете всей необходимой информацией, то, возможно, пришло время остановиться и пересмотреть все пройденные этапы, прежде чем утверждать, что требования ошибочны.
  • Нельзя не соглашаться по субъективным причинам. Обращайте внимание на подлинные потенциальные проблемы.
    «Мне не нравится, как это сделано» — субъективное суждение. «Это приведет к проблемам с производительностью, поскольку очень много операций вовлечено» — объективная причина. Примеры других субъективных причин: «А на другом проекте мы это делали иначе» или «Я бы реализовал это решение чуточку иначе, но это, конечно, дело вкуса».
  • У вас должны быть наготове хорошо продуманные объяснения, подкрепляющие ваши претензии.
    Если вы не можете объяснить, почему что-то неверно — то можете ли быть уверены, что действительно правы? Советую выписать причины, по которым решение кажется вам неверным, и сформулировать, каким образом его можно исправить.


Если же вы такого решения предложить не можете — с самого начала четко об этом скажите.

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

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

Мне нравится начинать дискуссию словами: «Не то, что я с вами не согласен, просто я пока не понимаю.» Позже при необходимости можно выразить решительное несогласие, но для начала — во всем разобраться.

Умейте не соглашаться правильно


Чтобы гарантировать, что наше несогласие будет объективным, примем ряд мер, которые помогут нам понять, правомерны ли наши доводы.

Объективное несогласие позволяет продемонстрировать как минимум один из следующих фактов:

  • Решение является недостаточно информированным
  • Решение является превратно информированным
  • Задача или решение нелогичны
  • Решение является неполным


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

Быть превратно информированным означает создавать решение, исходя из неверной информации.

Речь о случае, когда, по мнению составителей ТЗ, имеющая система может что-либо сделать, однако в реальности в ней эта возможность не предусмотрена. Например, команда SEO попросила вас добиться, чтобы Google индексировал страницу с учетной записью пользователя в вашем приложении. Google этого делать не может. Значит, ваши сеошники неверно представляют себе функции поискового робота Google.

Нелогичная задача или нелогичное решение попросту лишены смысла. Типичный пример (с точки зрения разработчика) — реализовать фичу, которая обрушит какую-то другую фичу. Такое требование можно счесть нелогичным, поскольку оно скорее навредит, чем поможет.

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

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

© Habrahabr.ru