Функции: эта ошибка дороже, чем «null»

Kate

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

Имя этому шаблону — функция.

Если конкретнее, то это интерфейс, который обычно является набором функций.

lhnvsqayh4cmmht64aoa36gcafa.png


Какие из языков вы узнали?

Одним из первых при изучении программирования мы узнаём принцип многократного использования логики. Это неизменно приводит нас к функции — строительному блоку каждого программного проекта. Сами по себе функции не так уж плохи, но именно из-за использования их в качестве многократно применяемого компонента написание, поддержка и масштабирование ПО становятся такими затратными.

Почему?
Писать многократно используемый код сложно.

Нет, это не так. Это неприемлемо.

Любой разработчик может писать многократно используемый код, вне зависимости от своего опыта. Языки наподобие JavaScript, Python, Ruby и Go составлены из миллионов маленьких общих модулей, демонстрирующих простоту написания простого многократно используемого исходного кода. Писать многократно используемый код легко.

Давайте подвергнем это утверждение рефакторингу.

Писать многократно используемый код сложно. Сложно многократно использовать код.

Но и это не так. Взгляните на библиотеку node.js repeat-string в npm. Она не делает ничего, кроме повторения строки, и разработчики скачивают её каждую неделю по семнадцать миллионов раз.

fsc71othd2-7pthuujlx_dl129o.png


Количество скачиваний repeat-string в npm

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

Так к чему это я?

Как можно найти модуль наподобие repeat-string для своего проекта на node.js? Нужно поискать «repeat string» в npm. Может быть, вы введёте «string repeat», но результаты будут похожими. Проблему, о которой я говорю, можно заметить во втором поисковом результате. И в четвёртом, и в девятом, и в десятом, и в одиннадцатом.

Посмотрите на эти примеры. Каждая библиотека обеспечивает совершенно идентичное поведение.

uz9xfbi3-eoh1a2a8ysgiygsn64.png


Примеры библиотек повторения строк в npm

Вы видите, в чём проблема?

Нет, не в том, что одна из них по каким-то непонятным причинам асинхронна. Не говорю я и о том, что повторение строки уже в течение шести с лишним лет является частью языка JavaScript ("A".repeat(5)). Проблема в том, что каждая библиотека имеет свои отличия:

  1. В сигнатуре входящих данных. Некоторые могут получать (string, int), другие — (int, string). Одна принимает числа с плавающей запятой, другой требуется функция обратного вызова.
  2. В сигнатуре выходных данных. Каждая библиотека выводит строку, за исключением одной, которая ничего не выводит и передаёт свой результат в функции обратного вызова. И позвольте мне даже не начинать разбор их ошибок.
  3. В поведении при их выполнении. Одна асинхронна, остальные синхронны.
  4. По способу предоставления доступа. Некоторые предоставляют доступ к одной экспортированной функции, другие предоставляют функцию как метод объекта.

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

А почему это проблема?​


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

Как часто нам нужно что-то менять? Каждый день. Именно этим мы и занимаемся. Как часто ПО из-за этого ломается? Буквально каждый раз. Иногда поломка незначительна, и мы устраняем её без особых усилий. Мы смирились с тем, что поломанное ПО — это нормально. Даже когда мы пишем автоматизированные тесты, которые не делают ничего, кроме проверки того, что что-то сломалось.

Проблема с интерфейсами произрастает из того факта, что программист в процессе разработки может принимать множество решений. Таких решений, как выбор между позиционными параметрами, конфигурациями и компоновщиками, асинхронностью и синхронностью, глобальным и локальным, stateful и stateless, конструкторами и фабриками, функциональным и объектно-ориентированным подходом, а также из бесконечного количества других вариантов. Каждый выбор определяется современными best practices и каждый становится песчинкой, попавшей внутрь механизма.

Эта проблема не нова, однако мы миримся с ней и обучаем такому подходу каждое новое поколение. Почему? Проблема не в том, что мы принимаем неверные решения, а в том, что нам предоставлены плохие варианты. Каждый вариант — это калейдоскоп компромиссов, и правильный ответ зависит от точки зрения. Когда всё представляет собой компромисс, вариант получше есть всегда. Всегда можно переписать код, сделав его лучше.

Давайте подвергнем наше утверждение рефакторингу в третий раз.

Писать многократно используемый код сложно. Сложно многократно использовать код. Сложно писать заменяемый код.

Это утверждение не так привлекательно, зато ближе к правде.

Без кода, который можно просто вставить, нам нужно проглаживать каждую складку интерфейса, прежде чем можно будет его использовать. Нужно настраивать каждый фрагмент кода, который попадёт в приложение. Каждый ввод. Каждый вывод. Каждый API. Мы засовываем компоновщики на адаптеры и обёртываем их в фабрики на основе сервисов. Но никакое количество грима не сделает ваш паттерн проектирования красивым.

Мы как будто создаём ПО в 18-м веке. Мы вручную распиливаем деревья на доски произвольного размера. Мы изготавливаем с нуля молотки и штампуем гвозди, просто чтобы построить дом, в точности похожий на соседний. Это стоит слишком дорого и занимает слишком много времени. Даже после завершения проекта нас всё равно тянет к земле бремя поддержки. Размеры нестандарты, проводка бьёт электриков током, а строителям, только что выпустившимся из ремесленного училища, не нравится, как мы сделали гвозди. Уже давно настала пора для появления «Леруа-Мерлен» и цифровых 2x4 в мире ПО.

oy82z-naxbjpf1msznuzlwwnktk.png


«Привет, меня зовут Тим. Я работаю техлидом в Google, имею 30-летний опыт кодинга, но вынужден искать, как получить длину строки в Python».

Справку по какому API постоянно приходится искать лично вам?


Не станет неожиданностью, если я скажу, что у нас могли бы быть стандарты, достаточно хорошо подходящие для каждого. Microsoft внедрила в 90-х технологию COM, чтобы обмениваться логикой между приложениями, написанными на любом языке. Технологии JVM почти столько же лет, и она продемонстрировала, как множество языков может обрабатывать один и тот же байт-код, а также взаимодействовать друг с другом. Мир продолжает десятками лет заново открывать для себя Flow и Linda как способ связывания друг с другом распределённых «чёрных ящиков», а Docker стал новым взглядом на то, каким может быть современный «чёрный ящик».

Эта проблема предоставляет кучу возможностей, и многие пытаются её решить. Многообещающими новыми способами решений выглядят Dapr и WasmCloud. Они частично решают проблему разными способами. Как бы ни было печальным состояние разработки ПО сегодня, у меня сегодня больше надежд, чем в прошлые 10 лет. Краткий период восхитительных перспектив возник благодаря JavaScript, обещавшему создание универсальной платформы, на которой аморфные приложения смогут распространиться по всему миру, но в результате он превратился в кучу Electron, React и потраченной впустую ОЗУ. Для меня новым источником оптимизма стал Web Assembly. Он далёк от идеала, но это и замечательно. Идеал никогда не побеждает.


Источник статьи: https://habr.com/ru/company/vdsina/blog/559704/
 
Сверху