
Някой ми зададе интересен въпрос наскоро: „Как оправдаваше толкова големия риск да строиш Storm, докато работиш в стартъп?“ (Storm е система за изчисления в реално време.) Отстрани инвестицията в толкова мащабен проект изглежда изключително рискована за стартъп. От моя гледна точка обаче строителството на Storm изобщо не беше рисковано. Беше предизвикателство — но не риск.
Следвам стил на разработка, който силно намалява риска при големи проекти като Storm. Наричам го „програмиране, ориентирано към страданието“ (suffering-oriented programming). Може да се обобщи така: не гради технология, освен ако не усещаш болката от това, че я нямаш. Важи и за големите архитектурни решения, и за по-малките ежедневни избори в кода. Този подход намалява риска, защото винаги работиш по нещо важно — и защото познаваш добре проблемната област, преди да направиш голяма инвестиция.
Имам мантра за програмирането, ориентирано към страданието:
„Първо го направи възможно. После — красиво. После — бързо.“
Първо го направи възможно
Когато срещнеш непозната проблемна област, е грешка да се опиташ веднага да построиш „общо“ или „разширяемо“ решение. Просто още не разбираш областта достатъчно добре, за да предвидиш бъдещите нужди. Ще направиш общи неща, които не е нужно да са общи — ще добавиш сложност и ще губиш време.
По-добре е просто да „хакнеш“ решението и да се фокусираш директно върху конкретните проблеми пред теб. Така свършваш нужното и избягваш излишна работа. Докато хакваш, научаваш все повече за тънкостите на пространството от проблеми.
Фазата „направи го възможно“ за Storm беше една година хакване на система за обработка на потоци с опашки и работници. Научихме се да гарантираме обработката на данни с „ack“ протокол. Научихме се да мащабираме изчисленията в реално време с клъстери от опашки и работници. Научихме, че понякога потокът от съобщения трябва да се партиционира по различен начин — понякога на случаен принцип, понякога с hash/mod техника, която праща една и съща същност винаги към един и същ работник.
Дори не знаехме, че сме във фазата „направи го възможно“. Просто градехме продуктите си. Болката от системата с опашки и работници обаче бързо стана остра. Мащабирането беше досадно, а устойчивостта на грешки — далеч от желаното. Беше ясно, че парадигмата „опашки и работници“ не е на правилното ниво на абстракция: повечето ни код се занимаваше с маршрутизиране на съобщения и сериализация, а не с бизнес логиката, която ни интересуваше.
Междувременно развитието на продукта ни откри нови случаи на употреба в пространството „изчисления в реално време“. Направихме функционалност, която изчислява reach на URL в Twitter — броя уникални хора, които са видели даден URL. Това е трудна сметка: стотици заявки към базата и десетки милиони импресии само за едно изчисление. Оригиналната реализация на една машина отнемаше над минута при трудни URL-и и беше ясно, че ни трябва разпределена система, за да паралелизираме сметката и да я ускорим.
Едно от ключовите прозрения, които запалиха Storm, беше, че проблемът „reach“ и проблемът „обработка на потоци“ могат да се обединят чрез една проста абстракция.
После го направи красиво
Докато изследваш пространството с хакване, си изграждаш „карта“ на проблемите. С времето трупаш все повече случаи на употреба и дълбоко разбиране за тънкостите на тези системи. Това разбиране води създаването на „красива“ технология, която заменя старите системи, облекчава страданието ти и отваря нови системи/функции, които преди бяха прекалено трудни.
Ключът към „красивото“ решение е най-простият набор от абстракции, които решават конкретните случаи, които вече имаш. Грешка е да се опитваш да предвидиш случаи, които реално нямаш — иначе ще преинженерираш. Правило: колкото по-голяма е инвестицията, толкова по-дълбоко трябва да разбираш областта и толкова по-разнообразни трябва да са случаите ти. Иначе рискуваш ефекта на втората система.
„Да го направиш красиво“ е моментът, в който използваш уменията си за дизайн и абстракция, за да сведеш пространството до прости, композируеми абстракции. Гледам на това като на статистическа регресия: имаш точки на графиката (случаите на употреба) и търсиш най-простата крива, която минава през тях (набор от абстракции).

Колкото повече случаи имаш, толкова по-добре ще намериш правилната крива. Ако точките са малко, вероятно ще overfit-неш или underfit-неш — излишна работа и преинженериране.
Голяма част от „красивото“ е разбирането на производителността и ресурсните характеристики на областта. Това е една от тънкостите, които учиш във фазата „направи го възможно“, и трябва да я използваш при дизайна на красивото решение.
При Storm сведох областта на изчисленията в реално време до малък набор от абстракции: streams, spouts, bolts и topologies. Измислих нов алгоритъм за гарантирана обработка на данни, който премахна нуждата от междинни message brokers — частта от системата, която носеше най-много сложност и страдание. Че и stream processing, и reach — два привидно много различни проблема — се нанасят толкова елегантно върху Storm, беше силен знак, че съм на нещо голямо.
Предприех и допълнителни стъпки, за да събера още случаи на употреба и да валидирам дизайна. Разпитах други инженери за конкретиката на realtime проблемите им. Не питах само познати — туитнах, че работя по нова realtime система и искам да чуя чужди use cases. Това доведе до много интересни разговори, които ме образоваха повече за областта и потвърдиха идеите ми.
После го направи бързо
След като красивият дизайн е на място, можеш спокойно да инвестираш време в profiling и оптимизация. Оптимизацията твърде рано е губене на време — защото може още да преосмислиш дизайна. Това се нарича premature optimization.
„Да го направиш бързо“ не е за високоуровневите характеристики на производителността. Разбирането за тях трябва да е дошло във фазата „направи го възможно“ и да е вградено във фазата „направи го красиво“. „Бързо“ тук означава микрооптимизации и по-ефективно използване на ресурсите. За асимптотичната сложност се грижиш в „красивото“; за константните фактори — в „бързото“.
Измий и повтори
Програмирането, ориентирано към страданието, е непрекъснат процес. Красивите системи, които градиш, ти дават нови възможности — и така можеш отново да „направиш възможно“ още по-дълбоки области. Това връща знание обратно в технологията. Често трябва да пипаш или да добавяш към вече измислените абстракции, за да покриеш все повече случаи.
Storm мина през много такива итерации. Когато започнахме да го ползваме, открихме нуждата един компонент да емитира множество независими потоци. Открихме, че специален вид поток — „direct stream“ — позволява на Storm да обработва партиди от tuples като конкретна единица. По-късно разработих „transactional topologies“, които надграждат гаранцията at-least-once и позволяват exactly-once семантика за почти произволни realtime изчисления.
По природа хакването в област, която още не разбираш добре, и постоянното итериране могат да доведат до разхвърлян код. Най-важната черта на програмиста, ориентиран към страданието, е безмилостният фокус върху рефакторинг. Това е критично, за да не саботира accidental complexity кодовата база.
Заключение
Use cases са всичко в програмирането, ориентирано към страданието. Тежат колкото злато. Единственият начин да ги придобиеш е чрез опит — чрез хакване.
Повечето програмисти минават през определена еволюция. Започваш с борба просто да проработи нещо — без структура, с разхвърлян код и copy/paste. После научаваш ползите от структурираното програмиране и споделянето на логика. После — за генерични абстракции и енкапсулация, за да е по-лесно да разсъждаваш за системите. После се обсебваш да правиш всичко генерично и разширяемо, за да „future-proof“-неш програмите си.
Програмирането, ориентирано към страданието, отхвърля идеята, че можеш ефективно да предвидиш нужди, които още нямаш. Признава, че опитите за генеричност без дълбоко разбиране на областта водят до сложност и разхищение. Дизайните винаги трябва да се водят от реални, осезаеми случаи на употреба.
Автор: Nathan Marz
Източник: „Suffering-oriented programming,“ thoughts from the red planet, 6 февруари 2012
Оригинал: https://nathanmarz.com/blog/suffering-oriented-programming.html