Программный практицизм.
В данной работе я пытаюсь ввести в оборот понятие «программный практицизм», и дать ему более-менее формальное определение, но в то-же время простым читаемым языком.
Сначала базовые понятия используемые по ходу этой статьи, и служащие её целям и только им. Данные понятия валидны и имеют смысл только в контексте данной статьи.
Инструмент.
Инструмент это повторно, многократно используемый код, как правило библиотека, компилятор, БД, утилита командной строки, фреймворк и т.п.
Язык.
Язык это понятийный аппарат, которым пользуется человек использующий данный инструмент. Например язык команды ls(или dir) это набор слов, на первом месте в котором будут слова файл и директория.
АПИ.
АПИ это программный код, который пользователь должен набрать, чтобы получить доступ к возможностям предоставляемым инструментом.
БАЛ.
БАЛ это сокращение от «базовый алгоритм», например, алгоритм быстрой сортировки, двоичное дерево. Некая функция — вокруг которой строится весь остальной функционал инструмента. Или набор таких функций. Как правило БАЛ имеет не много кода, но очень сложного, порой совершенствуемого десятками лет академическими учёными, профессионалами и хоббиистами всего мира.
Клей.
Весь остальной код инструмента, который предоставляет АПИ, соединяет БАЛ и АПИ. Именно клей обычно составляет основную массу кода в инструменте. Например в упомянутой команде ls БАЛы это функции readdir() и fstat(), они собственно и читают файлы и директории. Всё остальное это клей, обеспечивающий десятки ключей командной строки (АПИ), форматирование вывода (хотя, разбивание списка имён файлов на колонки тоже, несомненно, БАЛ) и всё богатство АПИ и языка этой команды.
Итак, программный практицизм заключается в БАЛ-центричности.
Правило первое: клея должно быть как можно меньше.
В идеале клея должно быть ровно столько, чтобы предоставить пользователю доступ к БАЛам, не больше и не меньше.
Правило второе: АПИ должно быть как можно ближе к интерфейсу БАЛов на которых строится данный инструмент. Обычно бывает наоборот, сначала придумывается АПИ, для решения своих задач, потом ищутся БАЛы на основе которых можно такой инструмент написать, иногда даже выкидывается один БАЛ и вместо него ставится другой, сохраняя АПИ неизменным или минимально его модифицируя, обычно потому, что новый БАЛ просто требует новых параметров или функций.
Например, написали некий контейнер, для него АПИ, и построили его вокруг массива, потом оказалось, что связанный список лучше подойдёт для задачи, заменили массив связанным списком, но поскольку связанный список предоставляет удобство движения вперёд и назад, добавили функции next()/prev() которых ранее не было. Получается, что АПИ с одной стороны строится исходя из внешних соображений, языка пользователя, предполагаемых применений, а с другой стороны, зависит от внутреннего устройства инструмента, в первую очередь от используемых БАЛов.
Правило третье: БАЛы должны быть high-end, то есть лучшее из того, что можно достать.
Это правило прямая противоположность часто применяемому подходу, когда основные усилия тратятся на разработку клея, а на то, чтобы сделать, скажем, очень эффективный, мирового класса двоичный поиск времени нет, в результате пишется на коленке БАЛ, подгоняется под весь «остальной» код. Из-за своей неэффективности, он не может использоваться везде, и в тех местах, где он не подходит, пишется что-то ещё. Основная логика разработки держится в клее, а БАЛы приделываются с разных сторон, чтобы эту махину поддерживать. Или берётся с GitHub алгоритм мирового класса, но поскольку он неидеально подходит к решениям уже принятым в инструменте, от допиливается или обвешивается врапперами. Вместо того, чтобы:
Правило четвёртое: весь код (или клей в терминологии данной статьи) должен быть балоцентричным. Если обнаруживается несовместимость БАЛа и вашего доморощенного кода, БАЛ воспринимается, как арбитр, как высшая инстанция, и код меняется, так, чтобы соответствовать особенностям используемого базового алгоритма.
Правило пятое: язык на котором рассуждают пользователи инструмента должен включать в себя основные принципы базовых алгоритмов. И, минимум всего остального.
То есть вместо «телефон», «база», «адреса», «посмотреть во всех» и прочее, вы не только предоставляете АПИ по принципу ключ/значение, но и пользователь должен рассуждать на том же языке, на котором строятся базовые алгоритмы. Ибо эффективное использование базового алгоритма, возможно только в рамках понятийного аппарата данного базового алгоритма. Проще говоря, если вы, как пользователь, не знаете, что такое чёрно-красное дерево, а именно на нём строится используемый вами инструмент, то инструмент не будет использоваться достаточно эффективно.
Можно постоянно наблюдать желание программистов спрятать подробности имплементации под капот и дать человеку руль и педаль. Но вместе с рулём и педалями, надо ему дать и язык с основными понятиями «диск сцепления», «сцепление с дорогой», «тормозной путь». Игнорирование понимания устройства ведёт к аварийности.
Итак, в результате вся деятельность в разработке инструмента концентрируется вокруг базовых алгоритмов, внедрения лучших образцов таковых или разработка своих уникальных. Работа становится намного более техничной и разговоры становятся более конкретными.
В качестве хорошего примера программного практицизма можно привести DB Redis.
Вместо привычного, более «удобного» абстрактного языка он даёт АПИ где даже в названиях команд отражены используемые алгоритмы: Set, List, Key/Value(Hash table). Хотя можно было согласно принципу 5, ZList назвать Skiplist потому что, именно на алгоритме скиплиста строится ZList и понимание этого факта пользователем сделало бы его применение ещё более эффективным. И больше там нет ничего совсем, есть БАЛы, базовые алгоритмы, и набор функций к ним.
Можно даже такое сказать: если у вас есть клиент и у него есть задачи которые хорошо решаются неким базовым алгоритмом, вы должны научить клиента говорить на языке этого алгоритма, а не наоборот.
Практицизм, это не обязательно минимализм, особенно когда в инструменте соединяется несколько БАЛов и серьёзной задачей становится предоставить пользователю консистентный доступ сразу ко всем их возможностям. Как в том же Redis можно в одном запросе использовать Set и List. В таком случае код соединяющий несколько БАЛов может стать сам по себе сложной разработкой и вылиться в результате в новый БАЛ. Как, например, LZ компрессия это два базовых алгоритма, поиск повторов методом скользящего окна и сжатие по Хаффману, и потребовалась большая работа, чтобы соединить эти два алгоритма в третий, который теперь является стандартным БАЛом множества систем.
Кстати, хороший пример, gzip вполне себе инструмент в духе практицизма, он только лишь даёт минимальный доступ к функции gzip(), в отличие от утилиты pkzip которая обросла огромным количеством функций. Конечно сравнение тут лишь частичное, ведь pkzip имеет два ключевых базовых алгоритма, сжатие и архивация множества файлов, а gzip только сжатие.
В минимализме другое правило: если в вашем АПИ есть редко используемые возможности, уберите их. Это совершенно другой подход, и часто он ведёт к своим проблемам. Особенность практицизма, как раз в том, что вы предоставляете полный набор функций ассоциируемых с базовыми алгоритмами.
Если вы запутались в разработке собственного инструмента, попробуйте вытряхнуть из него всё кроме базовых алгоритмов, и собрать заново по принципам «балоцентричного» программного практицизма.
Практицизм идёт немного не в ногу с мировым трендом под названием модульность, который обычно озвучивается так: «пусть инструмент делает одну вещь и делает её хорошо». Некоторое сходство с принципами практицизма можно усмотреть, но исходная точка разная, практицизм предлагает отталкиваться от самих базовых алгоритмов как они есть, исходя из того, что всё-равно, что бы вы ни делали, какой бы функционал не имплементировали, вы будете опираться на базовые алгоритмы в любом случае, вам от них никуда не деться. Возьмёте их из open source, из Википедии, или создадите оригинальные, логично строить систему вокруг них, чем постоянно с ними бороться.
Автор предлагает сменить фокус при разработке систем, самый трудоёмкий и ценный код это базовые алгоритмы, но как правило им уделяется весьма второстепенное внимание, вместо того, чтобы попробовать поставить их во главу угла и строить вокруг них всё остальное.
Резюмируя: меньше клея, продвинутее БАЛы, любой конфликт между ними в пользу БАЛов, АПИ и терминология напрямую связаны с БАлами.
Автор данной статьи не утверждает, что подобный подход к программированию самый лучший, или лучше других, что это какая-то истина, просто это ещё один подход, вполне зарекомендовавший себя с чёткими правилами и понятийным аппаратом.
В данной работе я пытаюсь ввести в оборот понятие «программный практицизм», и дать ему более-менее формальное определение, но в то-же время простым читаемым языком.
Сначала базовые понятия используемые по ходу этой статьи, и служащие её целям и только им. Данные понятия валидны и имеют смысл только в контексте данной статьи.
Инструмент.
Инструмент это повторно, многократно используемый код, как правило библиотека, компилятор, БД, утилита командной строки, фреймворк и т.п.
Язык.
Язык это понятийный аппарат, которым пользуется человек использующий данный инструмент. Например язык команды ls(или dir) это набор слов, на первом месте в котором будут слова файл и директория.
АПИ.
АПИ это программный код, который пользователь должен набрать, чтобы получить доступ к возможностям предоставляемым инструментом.
БАЛ.
БАЛ это сокращение от «базовый алгоритм», например, алгоритм быстрой сортировки, двоичное дерево. Некая функция — вокруг которой строится весь остальной функционал инструмента. Или набор таких функций. Как правило БАЛ имеет не много кода, но очень сложного, порой совершенствуемого десятками лет академическими учёными, профессионалами и хоббиистами всего мира.
Клей.
Весь остальной код инструмента, который предоставляет АПИ, соединяет БАЛ и АПИ. Именно клей обычно составляет основную массу кода в инструменте. Например в упомянутой команде ls БАЛы это функции readdir() и fstat(), они собственно и читают файлы и директории. Всё остальное это клей, обеспечивающий десятки ключей командной строки (АПИ), форматирование вывода (хотя, разбивание списка имён файлов на колонки тоже, несомненно, БАЛ) и всё богатство АПИ и языка этой команды.
Итак, программный практицизм заключается в БАЛ-центричности.
Правило первое: клея должно быть как можно меньше.
В идеале клея должно быть ровно столько, чтобы предоставить пользователю доступ к БАЛам, не больше и не меньше.
Правило второе: АПИ должно быть как можно ближе к интерфейсу БАЛов на которых строится данный инструмент. Обычно бывает наоборот, сначала придумывается АПИ, для решения своих задач, потом ищутся БАЛы на основе которых можно такой инструмент написать, иногда даже выкидывается один БАЛ и вместо него ставится другой, сохраняя АПИ неизменным или минимально его модифицируя, обычно потому, что новый БАЛ просто требует новых параметров или функций.
Например, написали некий контейнер, для него АПИ, и построили его вокруг массива, потом оказалось, что связанный список лучше подойдёт для задачи, заменили массив связанным списком, но поскольку связанный список предоставляет удобство движения вперёд и назад, добавили функции next()/prev() которых ранее не было. Получается, что АПИ с одной стороны строится исходя из внешних соображений, языка пользователя, предполагаемых применений, а с другой стороны, зависит от внутреннего устройства инструмента, в первую очередь от используемых БАЛов.
Правило третье: БАЛы должны быть high-end, то есть лучшее из того, что можно достать.
Это правило прямая противоположность часто применяемому подходу, когда основные усилия тратятся на разработку клея, а на то, чтобы сделать, скажем, очень эффективный, мирового класса двоичный поиск времени нет, в результате пишется на коленке БАЛ, подгоняется под весь «остальной» код. Из-за своей неэффективности, он не может использоваться везде, и в тех местах, где он не подходит, пишется что-то ещё. Основная логика разработки держится в клее, а БАЛы приделываются с разных сторон, чтобы эту махину поддерживать. Или берётся с GitHub алгоритм мирового класса, но поскольку он неидеально подходит к решениям уже принятым в инструменте, от допиливается или обвешивается врапперами. Вместо того, чтобы:
Правило четвёртое: весь код (или клей в терминологии данной статьи) должен быть балоцентричным. Если обнаруживается несовместимость БАЛа и вашего доморощенного кода, БАЛ воспринимается, как арбитр, как высшая инстанция, и код меняется, так, чтобы соответствовать особенностям используемого базового алгоритма.
Правило пятое: язык на котором рассуждают пользователи инструмента должен включать в себя основные принципы базовых алгоритмов. И, минимум всего остального.
То есть вместо «телефон», «база», «адреса», «посмотреть во всех» и прочее, вы не только предоставляете АПИ по принципу ключ/значение, но и пользователь должен рассуждать на том же языке, на котором строятся базовые алгоритмы. Ибо эффективное использование базового алгоритма, возможно только в рамках понятийного аппарата данного базового алгоритма. Проще говоря, если вы, как пользователь, не знаете, что такое чёрно-красное дерево, а именно на нём строится используемый вами инструмент, то инструмент не будет использоваться достаточно эффективно.
Можно постоянно наблюдать желание программистов спрятать подробности имплементации под капот и дать человеку руль и педаль. Но вместе с рулём и педалями, надо ему дать и язык с основными понятиями «диск сцепления», «сцепление с дорогой», «тормозной путь». Игнорирование понимания устройства ведёт к аварийности.
Итак, в результате вся деятельность в разработке инструмента концентрируется вокруг базовых алгоритмов, внедрения лучших образцов таковых или разработка своих уникальных. Работа становится намного более техничной и разговоры становятся более конкретными.
В качестве хорошего примера программного практицизма можно привести DB Redis.
Вместо привычного, более «удобного» абстрактного языка он даёт АПИ где даже в названиях команд отражены используемые алгоритмы: Set, List, Key/Value(Hash table). Хотя можно было согласно принципу 5, ZList назвать Skiplist потому что, именно на алгоритме скиплиста строится ZList и понимание этого факта пользователем сделало бы его применение ещё более эффективным. И больше там нет ничего совсем, есть БАЛы, базовые алгоритмы, и набор функций к ним.
Можно даже такое сказать: если у вас есть клиент и у него есть задачи которые хорошо решаются неким базовым алгоритмом, вы должны научить клиента говорить на языке этого алгоритма, а не наоборот.
Практицизм, это не обязательно минимализм, особенно когда в инструменте соединяется несколько БАЛов и серьёзной задачей становится предоставить пользователю консистентный доступ сразу ко всем их возможностям. Как в том же Redis можно в одном запросе использовать Set и List. В таком случае код соединяющий несколько БАЛов может стать сам по себе сложной разработкой и вылиться в результате в новый БАЛ. Как, например, LZ компрессия это два базовых алгоритма, поиск повторов методом скользящего окна и сжатие по Хаффману, и потребовалась большая работа, чтобы соединить эти два алгоритма в третий, который теперь является стандартным БАЛом множества систем.
Кстати, хороший пример, gzip вполне себе инструмент в духе практицизма, он только лишь даёт минимальный доступ к функции gzip(), в отличие от утилиты pkzip которая обросла огромным количеством функций. Конечно сравнение тут лишь частичное, ведь pkzip имеет два ключевых базовых алгоритма, сжатие и архивация множества файлов, а gzip только сжатие.
В минимализме другое правило: если в вашем АПИ есть редко используемые возможности, уберите их. Это совершенно другой подход, и часто он ведёт к своим проблемам. Особенность практицизма, как раз в том, что вы предоставляете полный набор функций ассоциируемых с базовыми алгоритмами.
Если вы запутались в разработке собственного инструмента, попробуйте вытряхнуть из него всё кроме базовых алгоритмов, и собрать заново по принципам «балоцентричного» программного практицизма.
Практицизм идёт немного не в ногу с мировым трендом под названием модульность, который обычно озвучивается так: «пусть инструмент делает одну вещь и делает её хорошо». Некоторое сходство с принципами практицизма можно усмотреть, но исходная точка разная, практицизм предлагает отталкиваться от самих базовых алгоритмов как они есть, исходя из того, что всё-равно, что бы вы ни делали, какой бы функционал не имплементировали, вы будете опираться на базовые алгоритмы в любом случае, вам от них никуда не деться. Возьмёте их из open source, из Википедии, или создадите оригинальные, логично строить систему вокруг них, чем постоянно с ними бороться.
Автор предлагает сменить фокус при разработке систем, самый трудоёмкий и ценный код это базовые алгоритмы, но как правило им уделяется весьма второстепенное внимание, вместо того, чтобы попробовать поставить их во главу угла и строить вокруг них всё остальное.
Резюмируя: меньше клея, продвинутее БАЛы, любой конфликт между ними в пользу БАЛов, АПИ и терминология напрямую связаны с БАлами.
Автор данной статьи не утверждает, что подобный подход к программированию самый лучший, или лучше других, что это какая-то истина, просто это ещё один подход, вполне зарекомендовавший себя с чёткими правилами и понятийным аппаратом.
Ogoun
Тут абстракции не просто протекли, их прям совсем не осталось…
4p4 Автор
Откуда это «абстракции потекли»? Впервые встречаю выражение.
Ogoun
Это про паттерны в основном говорят, если по терминологии поста, то какие бы красивые api не давал инструмент, то рано или поздно придется разбираться что за ними лежит и изучать внутреннее устройство инструмента. Вот тут можно почитать
А в посте. например это место:
>>Проще говоря, если вы, как пользователь, не знаете, что такое чёрно-красное дерево, а именно на нём строится используемый вами инструмент, то инструмент не будет использоваться достаточно эффективно.
Т.е. сразу приветствуется изучение потребителем заAPIшного пространства инструмента.
4p4 Автор
Действительно родственное понятие. Статья про дырявые, протекающие абстракции приводит немало примеров делающих причины для существования программного практицизма более очевидными.
Ogoun
Мне кажется стоит разделить подходы для прикладных и системных программистов. Ну или для разработки на высоком и низком уровне.
В бизнесе часто (очень часто) встречается ситуация, когда продают продукт, который еще не написан или только в процессе проектирования. И в этом случае прикладным программистам точно не стоит лезть за API, им нужен быстро накиданный, рабочий прототип.
Конечно если есть команда сформированная как описывал Брукс, и в ней есть 'инструментальщики', то программный практицизм как раз их уровень.