- Регистрация
- 12.04.17
- Сообщения
- 19.095
- Реакции
- 107
- Репутация
- 0
Понимание даже основ программирования может упростить деятельность человека. Помимо очевидных вещей, таких как развитие абстрактного мышления или способность разделять задачу на меньшие части, я предлагаю пойти ещё дальше и воспользоваться основными подходами к разработке. На примере создания классической логической игры, проводя аналогию между визуальным и традиционным программированиями, я и хочу показать, как навыки разработки могут помочь в решении прикладной задачи. Желающих подискутировать на тему или поиграть в «Быков и коров» и выиграть приз — зову под кат.
Меня зовут Женя и я backend разработчик в компании ManyChat. Некоторое время назад ко мне обратилась команда HR с просьбой реализовать знакомую многим с детства игру «Быки и коровы» на базе нашего продукта. Позвольте добавить немного контекста. ManyChat — это инструмент для автоматизации взаимодействия между бизнесами и их клиентами, позволяющий ответить на общие вопросы через Facebook, а с недавнего времени SMS и Email. «Быки и коровы» — это логическая игра, в ходе которой за несколько попыток необходимо отгадать загаданное число. Многие могут знать её по игре Fallout, где необходимо взломать компьютерный терминал. Более подробно с правилами можно ознакомиться в конце статьи.
Визуализация
Что мы делаем перед тем как писать код сложной задачи? Идём к доске с маркером или берём в руки карандаш и бумагу и рисуем блок-схему или UML диаграмму, ведь визуализация — это наиболее естественный способ представления и восприятия информации для человека. Flow Builder — сердце нашей системы, базируется на различных объектах, графических элементах и правилах, по которым они взаимодействуют друг с другом. Я сказал бы, что это логическое продолжение объектно-ориентированного программирования. Так что я отбросил привычные инструменты и сразу перешёл к конструированию логики общения бота с игроком.
Flow игры «Быки и коровы»
Это очень удобно, поскольку UML диаграмма и Flow Builder практически тождественны. На этом шаге уже сочетаются проектирование с программированием. За несколько итераций я накидал flow и обозначил места взаимодействия с пользователем и с API. Полученный результат несколько отличался от исходного представления в моей голове — визуализация предоставила возможность посмотреть на задачу с другой стороны. Это хорошо, так как изменения в диаграмму вносить проще и дешевле, чем в код. Как мне кажется, смысл этого подхода уходит корнями к поговорке «семь раз отмерь, один — отрежь». Инженерам он должен быть особенно знаком и понятен.
Разграничение ответственности
Что должно быть реализовано на стороне ManyChat, а что на стороне кода? Ответ на этот вопрос поначалу мне не казался очевидным. Для взаимодействия на нашей платформе есть два инструмента: Dynamic Block и External Request. Они практически идентичны за исключением того, что первый выполняет запрос на удалённый сервер и идёт дальше к следующей инструкции, а второй позволяет прежде сопоставить данные ответа с переменными бота. В одной из первых версий я воспользовался Dynamic блоком и реализовал отправку турнирной таблицы из метода API прямиком в Facebook. Казалась бы, в этом подходе нет ничего плохого. Но если посмотреть немного дальше и предположить, что игрок захочет играть с помощью SMS — коду придётся узнать о канале коммуникации конкретного игрока. Это не кажется необходимым. В следующей версии я перевёл flow на работу с External Request, добавил запись ответа в пользовательские переменные и удалил любое упоминание о канале из кода. Теперь код игры можно использовать в другом приложении и реализовать любой желаемый интерфейс взаимодействия: от консоли до мобильного приложения. Было это отделение представления от управляющей логики согласно шаблону MVC или выделение ограниченных контекстов согласно подходу DDD — решайте сами. Главное здесь то, что не смотря на отсутствие требования, на подсознательном уровне я определил потенциально узкое место системы, которое в будущем могло бы создать проблемы.
Именование переменных
Именование переменных — одна из двух главных проблем программирования. Помимо переменных не менее важным является именование блоков. Многие пользователи не придают этому значения, не успевают оглянуться как flow превращается в набор Copy 1, Copy 2 и так далее. Это кратно усложняет понимание. Всё как с плохим классом — невозможно разобраться что он делает, не зайдя внутрь. Более подробно эту мысль раскрыл мой коллега. Если заинтересовались, предлагаю ознакомиться с
Стив Макконнелл в своей книге «Совершенный код» писал, что имя должно полно и точно описывать представляемую сущность. Думаю, с этим спорить сложно. Например, для булевых переменных я предпочитаю использовать префикс is. В таком случае читающему код уже по первым двум буквам становится понятно с чем он имеет дело. Что касается длины — желательно, чтобы она не превышала 20 символов (Gorla, Benander, and Benander, 1990). Чем меньше область видимости переменной, тем короче может быть её имя. Но так как все переменные в ManyChat имеют глобальную область видимости, стоит воспользоваться пространством имён. Для «Быков и коров» я использовал начальный сегмент в виде двух заглавных букв BC, производных от Bulls and Cows. Учитывая вышесказанное, у меня получилось имя переменной BC Is Victory — достаточно уникально и исчерпывающе, на мой взгляд.
DRY, KISS
Игра работает и, казалось бы, можно выдохнуть с чувством выполненного долга. Но мы-то знаем, что дальше идёт этап self-review и рефакторинга. При более детальном разборе можно заметить, как обработка стоп-слов и победа ведут на похожие блоки — призывы игрока к некоторым действиям. При этом проигрыш по непонятной причине живет своей жизнью. Объединение этих мест в одно кажется логичным шагом, позволяющим избежать дублирования, что в свою очередь приводит к повышению консистентности поведения и простоте сопровождения. Теперь, в случае необходимости изменения, нужно внести в единственный блок системы без изменений в других, что свидетельствует о том, что принцип Don’t repeat yourself применяется успешно.
Flow с соблюдением принципа DRY
Однако, не стоит останавливаться на достигнутом. Комплексные решения сложны в сопровождении. При возникновении ошибки локализовать её будет непросто, особенно спустя время, когда выпадешь из контекста. У нового человека уйдёт не мало сил, чтобы разобраться, что к чему. А ознакомиться придётся с каждым узлом, дабы не упустить ничего важного. Поэтому стоит воспользоваться принципом Keep it simple, stupid и разнести все возможные действия по отдельным местам. После декомпозиции у меня получилось 5 довольно простых для понимания flow. В качестве приятного бонуса данный рефакторинг удовлетворил Single responsibility принцип. Каждый flow имеет одну ответственность и эта ответственность полностью инкапсулирована в нём. Обращение происходит а-ля Black box, а по характеру возникшей проблемы её можно локализовать быстрее.
Flow с соблюдением принципа KISS
YAGNI
В одной из первых версий автоматизации были наработки по рассылке напоминаний о заброшенной игре и инструкций по получению подарков победителями. Реализацию первой идеи усложнили ограничения со стороны Facebook — с недавнего времени ужесточились условия отправки сообщений за пределами окна в 24 часа. Вторая же оказалась недостаточно тривиальной для реализации со стороны кода. Оба вопроса решаемы, но количество необходимых усилий не оправдывалось потребностями одноразовой кампании. К тому же, спринтовые задачи наступали на пятки, поэтому было принято решение не реализовывать данный функционал. И тут на помощь пришел You aren't gonna need it принцип, цель которого заключается в воздержании от избыточной функциональности. Это нормальная ситуация, когда условия меняются, и в продукте появляются мертвые, более ненужные части. Удаление соответствующих блоков облегчило понимание назначения flow и в лишний раз напомнило о важности своевременного отказа от добавления новой функциональности, в которой нет непосредственной надобности.
Опытные разработчики знают, что любая фича нуждается в постоянной поддержке, от рефакторинга кода до актуализации документации и работы с обратной связью от пользователей. Поэтому к добавлению новой функциональности всегда стоит относиться с подозрением, задавая вопрос, действительно ли это необходимо? В конечном счёте на этом этапе можно сэкономить уйму ресурсов.
Давайте поиграем
Этапы self-review и рефакторинга пройдены, ненужного функционала нет, а смысл всех flow прозрачен для понимания. Это значит, что игра готова и настало время побороться за призы. По условиям конкурса топ-10 игроков, которые наберут наибольшее количество баллов до 22 мая 2020 года, получат призы от команды ManyChat.
Немного о правилах. В нашей реализации игра рассчитана на одного человека. Бот загадывает 4-значную последовательность с неповторяющимися цифрами. Игрок делает попытку отгадать число. Бот сообщает в ответ, сколько цифр угадано без совпадения с их позициями, то есть количество коров, и сколько угадано вплоть до позиции, то есть количество быков. В итоге сценарий игры может выглядеть примерно так:
Загадано число 1234.
Участник делает первое предположение: 4321
Получает ответ: 0 быков и 4 коровы
Участник делает второе предположение: 1678
И получает ответ: 1 бык и 0 коров.
Для того, чтобы попробовать, переходите по
Игра была реализована на стандартном стеке технологий: Nginx 1.17, PHP 7.4, PostgreSQL 12.1. При желании вы можете склонировать
Выводы
«Каждый в этой стране должен научиться программировать на компьютере, потому что это учит вас думать». Думаю, актуальность данного высказывания давно вышла за пределы только одной страны. Программирование — это не тайные символы в терминале, сложные для восприятия непосвященному человеку, а программист — не избранный. В первую очередь, это образ мышления и набор навыков для решения задач.
Обращаясь к названию статьи и оригинальному лозунгу, хочется перефразировать его: «Программирование принадлежит народу. Оно должно уходить своими глубочайшими корнями в самую толщу широких трудящихся масс. Оно должно быть понято этими массами и любимо ими».
Никогда бы не подумал, что в одной статье буду цитировать Стива Джобса и Ленина.
Я уверен, что мысли, изложенные здесь, приходили в голову не только мне. Некоторые даже сочтут их капитанскими. Но знаю по себе, что иногда нужно услышать одну и ту же информацию несколько раз, чтобы начать ей пользоваться. Банальный принцип — максимально упрощать и так несложные с виду вещи. Каждый программист знает, что код проекта достаточно быстро и легко выходит из под контроля, если не уделять этому должное внимание. Но иногда приходится снова напоминать себе об этом.
Что-то не было освещено вовсе. Например, в этой статье не были затронуты приёмы экстремального программирования, такие как парное программирование, частые небольшие релизы и игра в планирование. Хоть и гибкие методологии имеют общие корни с системами организации офлайновых, физических производств, они всё же зачастую применяются для разработки программного обеспечения. Хотя их потенциал не ограничивается отраслью.
Вероятно есть то, о чём я и не задумывался. У каждого из нас свой опыт и своё видение. Поэтому призываю вас поделиться в комментариях своими мыслями по этому поводу.

Меня зовут Женя и я backend разработчик в компании ManyChat. Некоторое время назад ко мне обратилась команда HR с просьбой реализовать знакомую многим с детства игру «Быки и коровы» на базе нашего продукта. Позвольте добавить немного контекста. ManyChat — это инструмент для автоматизации взаимодействия между бизнесами и их клиентами, позволяющий ответить на общие вопросы через Facebook, а с недавнего времени SMS и Email. «Быки и коровы» — это логическая игра, в ходе которой за несколько попыток необходимо отгадать загаданное число. Многие могут знать её по игре Fallout, где необходимо взломать компьютерный терминал. Более подробно с правилами можно ознакомиться в конце статьи.
Визуализация
Что мы делаем перед тем как писать код сложной задачи? Идём к доске с маркером или берём в руки карандаш и бумагу и рисуем блок-схему или UML диаграмму, ведь визуализация — это наиболее естественный способ представления и восприятия информации для человека. Flow Builder — сердце нашей системы, базируется на различных объектах, графических элементах и правилах, по которым они взаимодействуют друг с другом. Я сказал бы, что это логическое продолжение объектно-ориентированного программирования. Так что я отбросил привычные инструменты и сразу перешёл к конструированию логики общения бота с игроком.

Flow игры «Быки и коровы»
Это очень удобно, поскольку UML диаграмма и Flow Builder практически тождественны. На этом шаге уже сочетаются проектирование с программированием. За несколько итераций я накидал flow и обозначил места взаимодействия с пользователем и с API. Полученный результат несколько отличался от исходного представления в моей голове — визуализация предоставила возможность посмотреть на задачу с другой стороны. Это хорошо, так как изменения в диаграмму вносить проще и дешевле, чем в код. Как мне кажется, смысл этого подхода уходит корнями к поговорке «семь раз отмерь, один — отрежь». Инженерам он должен быть особенно знаком и понятен.
Разграничение ответственности
Что должно быть реализовано на стороне ManyChat, а что на стороне кода? Ответ на этот вопрос поначалу мне не казался очевидным. Для взаимодействия на нашей платформе есть два инструмента: Dynamic Block и External Request. Они практически идентичны за исключением того, что первый выполняет запрос на удалённый сервер и идёт дальше к следующей инструкции, а второй позволяет прежде сопоставить данные ответа с переменными бота. В одной из первых версий я воспользовался Dynamic блоком и реализовал отправку турнирной таблицы из метода API прямиком в Facebook. Казалась бы, в этом подходе нет ничего плохого. Но если посмотреть немного дальше и предположить, что игрок захочет играть с помощью SMS — коду придётся узнать о канале коммуникации конкретного игрока. Это не кажется необходимым. В следующей версии я перевёл flow на работу с External Request, добавил запись ответа в пользовательские переменные и удалил любое упоминание о канале из кода. Теперь код игры можно использовать в другом приложении и реализовать любой желаемый интерфейс взаимодействия: от консоли до мобильного приложения. Было это отделение представления от управляющей логики согласно шаблону MVC или выделение ограниченных контекстов согласно подходу DDD — решайте сами. Главное здесь то, что не смотря на отсутствие требования, на подсознательном уровне я определил потенциально узкое место системы, которое в будущем могло бы создать проблемы.
Именование переменных
Именование переменных — одна из двух главных проблем программирования. Помимо переменных не менее важным является именование блоков. Многие пользователи не придают этому значения, не успевают оглянуться как flow превращается в набор Copy 1, Copy 2 и так далее. Это кратно усложняет понимание. Всё как с плохим классом — невозможно разобраться что он делает, не зайдя внутрь. Более подробно эту мысль раскрыл мой коллега. Если заинтересовались, предлагаю ознакомиться с
You must be registered for see links
.Стив Макконнелл в своей книге «Совершенный код» писал, что имя должно полно и точно описывать представляемую сущность. Думаю, с этим спорить сложно. Например, для булевых переменных я предпочитаю использовать префикс is. В таком случае читающему код уже по первым двум буквам становится понятно с чем он имеет дело. Что касается длины — желательно, чтобы она не превышала 20 символов (Gorla, Benander, and Benander, 1990). Чем меньше область видимости переменной, тем короче может быть её имя. Но так как все переменные в ManyChat имеют глобальную область видимости, стоит воспользоваться пространством имён. Для «Быков и коров» я использовал начальный сегмент в виде двух заглавных букв BC, производных от Bulls and Cows. Учитывая вышесказанное, у меня получилось имя переменной BC Is Victory — достаточно уникально и исчерпывающе, на мой взгляд.
DRY, KISS
Игра работает и, казалось бы, можно выдохнуть с чувством выполненного долга. Но мы-то знаем, что дальше идёт этап self-review и рефакторинга. При более детальном разборе можно заметить, как обработка стоп-слов и победа ведут на похожие блоки — призывы игрока к некоторым действиям. При этом проигрыш по непонятной причине живет своей жизнью. Объединение этих мест в одно кажется логичным шагом, позволяющим избежать дублирования, что в свою очередь приводит к повышению консистентности поведения и простоте сопровождения. Теперь, в случае необходимости изменения, нужно внести в единственный блок системы без изменений в других, что свидетельствует о том, что принцип Don’t repeat yourself применяется успешно.

Flow с соблюдением принципа DRY
Однако, не стоит останавливаться на достигнутом. Комплексные решения сложны в сопровождении. При возникновении ошибки локализовать её будет непросто, особенно спустя время, когда выпадешь из контекста. У нового человека уйдёт не мало сил, чтобы разобраться, что к чему. А ознакомиться придётся с каждым узлом, дабы не упустить ничего важного. Поэтому стоит воспользоваться принципом Keep it simple, stupid и разнести все возможные действия по отдельным местам. После декомпозиции у меня получилось 5 довольно простых для понимания flow. В качестве приятного бонуса данный рефакторинг удовлетворил Single responsibility принцип. Каждый flow имеет одну ответственность и эта ответственность полностью инкапсулирована в нём. Обращение происходит а-ля Black box, а по характеру возникшей проблемы её можно локализовать быстрее.

Flow с соблюдением принципа KISS
YAGNI
В одной из первых версий автоматизации были наработки по рассылке напоминаний о заброшенной игре и инструкций по получению подарков победителями. Реализацию первой идеи усложнили ограничения со стороны Facebook — с недавнего времени ужесточились условия отправки сообщений за пределами окна в 24 часа. Вторая же оказалась недостаточно тривиальной для реализации со стороны кода. Оба вопроса решаемы, но количество необходимых усилий не оправдывалось потребностями одноразовой кампании. К тому же, спринтовые задачи наступали на пятки, поэтому было принято решение не реализовывать данный функционал. И тут на помощь пришел You aren't gonna need it принцип, цель которого заключается в воздержании от избыточной функциональности. Это нормальная ситуация, когда условия меняются, и в продукте появляются мертвые, более ненужные части. Удаление соответствующих блоков облегчило понимание назначения flow и в лишний раз напомнило о важности своевременного отказа от добавления новой функциональности, в которой нет непосредственной надобности.
Опытные разработчики знают, что любая фича нуждается в постоянной поддержке, от рефакторинга кода до актуализации документации и работы с обратной связью от пользователей. Поэтому к добавлению новой функциональности всегда стоит относиться с подозрением, задавая вопрос, действительно ли это необходимо? В конечном счёте на этом этапе можно сэкономить уйму ресурсов.
Давайте поиграем
Этапы self-review и рефакторинга пройдены, ненужного функционала нет, а смысл всех flow прозрачен для понимания. Это значит, что игра готова и настало время побороться за призы. По условиям конкурса топ-10 игроков, которые наберут наибольшее количество баллов до 22 мая 2020 года, получат призы от команды ManyChat.
Немного о правилах. В нашей реализации игра рассчитана на одного человека. Бот загадывает 4-значную последовательность с неповторяющимися цифрами. Игрок делает попытку отгадать число. Бот сообщает в ответ, сколько цифр угадано без совпадения с их позициями, то есть количество коров, и сколько угадано вплоть до позиции, то есть количество быков. В итоге сценарий игры может выглядеть примерно так:
Загадано число 1234.
Участник делает первое предположение: 4321
Получает ответ: 0 быков и 4 коровы
Участник делает второе предположение: 1678
И получает ответ: 1 бык и 0 коров.
Для того, чтобы попробовать, переходите по
You must be registered for see links
и следуйте подсказкам. Мы предлагаем 2 уровня сложности: игра на ограниченное и неограниченное число ходов. Победа в сложной принесёт вам 3 балла, в простой — 2. Проигрыш, независимо от уровня, заберёт 1 балл.Игра была реализована на стандартном стеке технологий: Nginx 1.17, PHP 7.4, PostgreSQL 12.1. При желании вы можете склонировать
You must be registered for see links
на свой сервер, установить
You must be registered for see links
на свою страницу ManyChat и устроить свой турнир.Выводы
«Каждый в этой стране должен научиться программировать на компьютере, потому что это учит вас думать». Думаю, актуальность данного высказывания давно вышла за пределы только одной страны. Программирование — это не тайные символы в терминале, сложные для восприятия непосвященному человеку, а программист — не избранный. В первую очередь, это образ мышления и набор навыков для решения задач.
Обращаясь к названию статьи и оригинальному лозунгу, хочется перефразировать его: «Программирование принадлежит народу. Оно должно уходить своими глубочайшими корнями в самую толщу широких трудящихся масс. Оно должно быть понято этими массами и любимо ими».
Никогда бы не подумал, что в одной статье буду цитировать Стива Джобса и Ленина.
Я уверен, что мысли, изложенные здесь, приходили в голову не только мне. Некоторые даже сочтут их капитанскими. Но знаю по себе, что иногда нужно услышать одну и ту же информацию несколько раз, чтобы начать ей пользоваться. Банальный принцип — максимально упрощать и так несложные с виду вещи. Каждый программист знает, что код проекта достаточно быстро и легко выходит из под контроля, если не уделять этому должное внимание. Но иногда приходится снова напоминать себе об этом.
Что-то не было освещено вовсе. Например, в этой статье не были затронуты приёмы экстремального программирования, такие как парное программирование, частые небольшие релизы и игра в планирование. Хоть и гибкие методологии имеют общие корни с системами организации офлайновых, физических производств, они всё же зачастую применяются для разработки программного обеспечения. Хотя их потенциал не ограничивается отраслью.
Вероятно есть то, о чём я и не задумывался. У каждого из нас свой опыт и своё видение. Поэтому призываю вас поделиться в комментариях своими мыслями по этому поводу.