Минимализм в ПО
Пока я выполняю второй модуль на стажировке, хотел бы написать небольшой очерк о моем представлении о минимализме в ПО, привести несколько примеров и аргументы, почему я считаю, что сложность современных программ чаще вредит, чем приносит пользу.
Что я подразумеваю под минимализмом в ПО? Моя идея основывается на философии Unix. Её первый пункт гласит:
Каждая программа должна выполнять одну задачу и делать это хорошо.
Зачастую, к слову, забывают следующий пункт:
Программы должны хорошо работать вместе.
Соответственно, первый и наиболее универсальный пример - это *nix-системы. Для людей, плохо с ними знакомых, приведу конкретный пример:
Допустим, у нас есть некоторая папка с большим количеством файлов. Мы хотим заархивировать 5 наиболее новых файлов, чье имя содержит слово backup. Первый приходящий в голову способ решения - открыть файловый менеджер, отсортировать файлы по дате изменения, вручную выбрать 5 файлов с подходящими именами и создать архив с помощью условного 7zip. Такой подход наиболее характерен для Windows-систем, где неискушенному пользователю доступен только такой функционал. Однако этот способ далёк от идеала. От пользователя требуется совершить довольно длинную последовательность действий - открыть нужную папку в Проводнике, выбрать сортировку, выбрать только те файлы, чье имя подпадает под критерий. А если выбрать требуется не 5, а 50 последних файлов? А если необходимо формировать такой архив регулярно? Нетрудно совершить ошибку.
Следующий логичный ответ - написать программу, которая бы выполняла эти действия за нас. В целом, это вполне адекватный подход, и сейчас существует немало языков программирования, которые позволяют писать такие утилиты "на коленке".
А что, если бы у нас уже был набор программ, выполняющих нужные нам действия, и мы могли бы собрать из них, как из конструктора, свою, соответствующую нашим требованиям?
В этом суть философии Unix. Вот, как выполнил бы вышеозначенную задачу опытный пользователь Linux:
shell
ls -t dir/ | grep "backup" | head -5 | xargs tar -czf
ls -t
выводит список файлов в директории dir
,
отсортированный по времени; символ |
, называемый "конвейер"
(pipeline), направляет вывод предыдущей команды в следующую.
grep
предназначен для поиска по тексту - он находит все
строки, содержащие подстроку "backup" и передает их дальше.
head -5
оставляет первые пять строк, из которых xargs
составляет список файлов, понимаемый архиватором tar
.
Если бы мы просто передали список файлов напрямую в
tar
, он бы воспринял полученный поток, как содержимое файла, которое надо добавить в архив, поэтому нам и нуженxargs
.
На первый взгляд, такой способ решения проблемы может
показаться чуть ли не сложнее первого. Однако вспомните - мы
хотим выполнять это действие регулярно (к тому же, поверьте
мне, опытный пользователь может составлять такие команды, не
сильно задумываясь). Для того, чтобы нам не приходилось каждый
раз вручную набирать команду, её можно просто сохранить в
текстовый файл, например, archive-latest
, после чего мы
можем вызывать её следующим образом: bash archive-latest
.
Такой способ взаимодействия программ через текстовый интерфейс был придуман и пропагандировался Дугом Макилроем, который принимал активное участие в разработке Unix и первых программ для него.
Хорошо, так значит, моя идея - использовать подход *nix-систем в разработке ПО? Не совсем. Это лишь одна из красивых идей, принадлежащих этой философии. Однако программы не обязательно должны работать таким образом; зачастую это попросту невозможно - где-то для ввода/вывода данных необходим графический интерфейс, где-то разработка ведётся под системы, не поддерживающие конвейеры.
Прежде чем более подробно оформить свою мысль, позволю себе привести ещё один пример.
Suckless - это команда разработчиков, которые с большим пиететом относятся к простоте программ и краткости кода. Большинство их инструментов и библиотек по количеству кода не превышают 1000 строк. Наиболее известные примеры: оконный менеджер dwm и терминал st, все написанные на Си - языке, который не имеет такого огромного количества встроенных функций и возможностей, но который, тем не менее, предоставляет своим пользователям куда бо́льший контроль над своими программами.
Разработчики suckless убеждены, что современные программы стали настолько сложными и медленными, что закон Мура об удвоении вычислительных мощностей каждый год в наши дни фактически неприменим - системные требования растут быстрее характеристик современных компьютеров.
Я сам ежедневно пользуюсь некоторыми их программами, хотя зачастую даже мне они кажутся слишком минимальными. Впрочем, никто не мешает самостоятельно расширить их функционал - на сайте suckless есть множество патчей, написанных другими пользователями, которые позволяют добавить недостающие фичи. Их софт - очень яркий пример минимализма в ПО.
Свою мысль, таким образом, я бы оформил так: со времён MS DOS и шестой редакции Unix требования к функционалу многих программ не сильно изменились. Текстовые редакторы, табличные процессоры, чаты в своей основе не делают ничего принципиально отличающегося от их далёких предков. Конечно, постоянно возникают новые требования к самим процессам, включающим в себя работу с этими программами - облачная синхронизация тому хороший пример. Однако эти требования не влияют непосредственно на предназначение программ - для редактирования текста нет необходимости в облаке.
Сложное, эмергентное поведение можно построить из элементарных компонентов, которые легко поддерживать, заменять и пересобирать под нужды пользователя. Более того, такой подход естественным образом подразумевает возможность использования End-user programming - он позволяет конечному пользователю самому изменять поведение своих программ, без необходимости глубоко изучать программирование или использовать некоторый избыточный интерфейс для такого взаимодействия.
Если пользователю нужна синхронизация файлов с облаком, он может самостоятельно добавить нужный для этого компонент. Если же ему это не нужно, то его программа не будет захламлена избыточным, ненужным функционалом. Пользователь может предпочесть красивый, анимированный интерфейс - или же использовать простой и практичный набор инструментов для взаимодействия с программой. Он может делиться готовыми комбинациями и настройками с другими пользователями. Вышеописанное уже встречается в среде пользователей Linux, однако не имеет такого широкого распространения, которого заслуживает.
Сейчас же пользователь вынужден подчиняться корпоративной логике, напрямую исходящей из соображений прибыли. Сложное ПО сложнее поддерживать, а о каких-либо коренных изменениях в структуре обычно не идёт и речи - и ошибки и недочёты копятся, подобно снежному кому. Зайдите достаточно глубоко в настройки WIndows - и наткнётесь на меню времен Windows XP, а то и Windows 2000. Сколько уязвимостей в древних, забытых всеми протоколах и службах находят ежемесячно? Конечно, уязвимости встречаются и в утилитах Linux - недавняя история с XZ тому пример. Однако у таких атак всегда меньше шансов на успех из-за большей площади поверхности, которую можно отслеживать.
Я, впрочем, не питаю ложных надежд касательно какого-либо коренного перелома в этом отношении в ближайшие годы. Однако сложность современных систем, на мой взгляд, взывает к новому взгляду на то, как проектируем ПО. Точно так же, как Netflix вытеснил кабельное ТВ во многих странах (а затем сам стал кабельным ТВ, но это другая история). Всё, что мы можем сейчас делать - это повышать компьютерную грамотность в надежде, что более эффективные принципы придут на смену более комплексным и расточительным.
P.S. Пока собирал и проверял информацию для этого очерка, наткнулся на очень подходящую цитату Дуга Макилроя:
The notion of "intricate and beautiful complexities" is almost an oxymoron. Unix programmers vie with each other for "simple and beautiful" honors — a point that's implicit in these rules, but is well worth making overt.
Теги: мысли, минимализм