Філософія програмування 7 - практицизм

Програмний практицизм.


У цій роботі я намагаюся ввести в обіг поняття «програмний практицизм», і дати йому більш-менш формальне визначення, але в той же час простою читаною мовою.

Спочатку базові поняття, які використовуються під час цієї статті, і ті, хто її працює, і лише мають. Дані поняття валідні і мають сенс тільки в контексті цієї статті.

Інструмент.

Інструмент повторно, багаторазово використовуваний код, як правило бібліотека, компілятор, БД, утиліта командного рядка, фреймворк тощо.

Мова.

Мова це понятійний апарат, яким користується людина, що використовує даний інструмент. Наприклад, мова команди 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, Вікіпедії, або створіть оригінальні, логічно будувати систему навколо них, ніж постійно з ними боротися.

Автор пропонує змінити фокус при розробці систем, найбільш трудомісткий і цінний код це базові алгоритми, але як правило їм приділяється досить другорядна увага, замість того, щоб спробувати поставити їх на перше місце і будувати навколо них все інше.

Резюмуючи: менше клею, просунутіше БАЛи, будь-який конфлікт між ними на користь БАЛів, АПІ і термінологія безпосередньо пов'язані з БАлами.

Автор цієї статті не стверджує, що подібний підхід до програмування найкращий, або краще за інших, що це якась істина, просто це ще один підхід, який цілком зарекомендував себе з чіткими правилами і понятійним апаратом.