Декомпозиция

Допустим у нас есть формула

integral formula

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

Оказывается что:

Интеграл от суммы равен сумме двух интегралов

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

Но почему-то мы раз за разом делаем это со своей жизнью. Мы берем неподъемную задачу и бьемся в неё головой.

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

Test Driven Development - разработка через тестирование

Я не хочу подробно рассказывать про TDD. Для этого пришлось бы написать книгу, а я могу навскидку порекомендовать две уже написанные: классика - Кент Бек: Экстремальное программирование. Разработка через тестирование и для программистов на языке C# - Рой Ошероув: Искусство автономного тестирования с примерами на С#. Но что если бы мне надо было продать вам TDD?

Я могу назвать 3 преимущества разработки через тестирование:

Рефакторинг

Первое (и это мне кажется чуть ли не самым важным) это рефакторинг. Кент Бек пишет, что задача рефакторинга не просто добиться некоей трудно измеряемой чистоты кода. Через рефакторинг мы устраняем дублирование! Я думаю, что минусы дублирования очевидны. Если я захочу изменить какой-то функционал, мне придется делать это в разных частях кодовой базы. Это лишний объем работы, к тому же очень легко пропустить один из дублей, тогда код, который должен быть одинаковым, будет обладать разным поведением. Чем меньше дублирования, тем проще модифицировать код. Модификация, это не только новый функционал, но и исправление багов в старом, и даже его оптимизация. Без развития, без модификации кодовая база умирает, а развитием очень тяжело заниматься без тестов. Так же тяжело, как в лоб решать пример, что я показывал выше, не научившись сначала решать его части. Но у этой проблемы есть еще более глубокий уровень. Далее Кент говорит, что дублирование плохо не только само по себе, оно является симптомом сильно связанного кода. Устраняя дублирование, мы устраняем и лишние зависимости.

Управляемый и предсказуемый процесс

Второе это определенные маленькие шаги. Так же как работает метод Pomodoro, можно декомпозировать трудную задачу на набор помельче и полегче, до тех пор пока не останутся такие, которые вы в состоянии решить минут за 15 (это условное число). Установите критерий корректности для каждой такой задачи, добейтесь, чтобы решение удовлетворяло критерию, соберите мелкие задачи в задачу побольше, установите критерий корректности для этой задачи и так шаг за шагом, пока исходная задача не будет решена. Это похоже на план. Для всех размер шага будет индивидуальным, даже для одного разработчика в зависимости от ситуации темп может быть разным и это здорово. Здорово, что можно выбрать свой темп, отталкиваясь от текущих условий. Главное, чтобы задача занимала небольшой промежуток времени и без особого напряжения помещалась в рабочей памяти. Если честно, я недоумеваю, почему по этому пункту приходится спорить (как и по первому). Почему разработчикам нравится сидеть и тратить часы на задачу, в которой ничего не понятно и она никак не хочет решаться? И даже если удастся ее решить, без тестов есть очень большой шанс, что к этому коду придется еще неоднократно вернуться, что там не все так очевидно.

Фреймворк

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

Не смогу выразить эту идею лучше, чем Майкл Физерс в предисловии к книге Роя Ошероува:

Когда-то давно машинное время стоило дорого. Программистам выделялось время для прогона их задач, они набивали программы на перфокартах и относили колоды в машинный зал. Если в программе была ошибка, то вы теряли выделенное вам время, поэтому приходилось, сидя за столом, мысленно на бумаге проигрывать все возможные сценарии, все граничные случаи. Сомневаюсь, что тогда сама идея автоматизированного автономного тестирования кому-то могла прийти в голову. Зачем использовать машину для тестирования, когда можно задействовать ее для решения задач, ради чего она и была построена? Из-за скудости ресурсов мы пребывали во мраке.

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

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