Научени уроци стартиране до $50 милиона приходи

Въведение

Ако сте като мен, страдате от умора от съвети. Има толкова много статии за процеси и управление, че е невъзможно да ги прочетете всички, камо ли да решите кои да следвате.

Това, което открих при стартирането на Collage.com от нула до $50 милиона приходи през последните няколко години, е, че процесите идват и си отиват, но причините, поради които умните инженери правят глупави неща, са по-вечни и универсални. Ако разбирате колко умни хора могат да взривят нещата чрез поредица от привидно логични решения, тогава ще разберете кога имате нужда от решение за управление и дали процесът du jour може да работи.

Темата на тази статия е локална срещу глобална оптимизация. Когато софтуерът е съвсем нов и се управлява от малък екип, е лесно да се вземат добри решения.

На този етап кодовата база е малка и вие не мислите за бъдещето, защото просто се опитвате да тръгнете от земята. С нарастването на екипа и узряването на продукта става все по-трудно да се види голямата картина при вземането на решения. Това е добре, когато местните цели са в съответствие с глобалните приоритети, като писане на висококачествен код. Но най-добрите местни решения понякога могат да бъдат вредни, когато се екстраполират в по-широк контекст с течение на времето.

Тук ще разгледаме няколко често срещани начина, по които този проблем може да се прояви.

Недостатъчно тегло Съгласуваност срещу Гъвкавост

Когато са изправени пред проблем, инженерите обичат да намерят най-доброто решение. Това обаче често изисква сложен анализ. Алтернативата е да се приложи обща евристика, която работи добре средно, но може да не винаги е най-добрата.

Тази ситуация се появява често с насоки за разработка на софтуер. Изкушаващо е да се правят изключения за смекчаващи обстоятелства - но това подкопава последователността. Цената на това може да бъде трудна за разбиране, тъй като ефектите често са фини и дългосрочни, докато ползите от гъвкавостта са по-очевидни и незабавни.

В миналото нямахме правило в Collage.com за това колко покритие от регресионен тест да добавим за нови функции. Оставихме на преценката на всеки разработчик дали да добави пълно покритие от самото начало или да стартира с ръчно тестване и планира да добави автоматизирани тестове по-късно.

Тази гъвкавост беше ценна при заобикалянето на проблеми с нашата инфраструктура за тестване, които можеха сериозно да забавят проектите. Стартирането дни или седмици по-рано може да ни помогне да повторим по-бързо, без да създаваме значително повече грешки, ако ръчното тестване беше задълбочено. В същото време в повечето случаи все пак добавихме регресионни тестове преди стартиране.

Въпреки че гъвкавостта на тестовото покритие направи възможни по-добри местни решения, несъответствието, което създаде, се отрази значително с течение на времето. Първо, гъвкавостта е по-добра само ако хората наистина вземат правилните решения; по-малко опитните инженери могат да направят повече грешки. За да бъде ценна гъвкавостта на решенията, трябва да се посочи кой може да взема решения, да се обучат тези хора, да се установи процес на преглед и да се оцени качеството на решенията като част от прегледите на ефективността. В края на деня хората от нашия екип понякога правеха лоши оценки относно покритието на тестовете и бяха нужни много усилия, за да се управлява тази гъвкавост.

Друга цена на гъвкавостта е, че води до много повече разходи за вземане на решения, които идват под формата на дискусии и документиране на решенията. Нашият екип прекара много време в разговори дали е най-добре да автоматизираме тестовете за всяка задача. След това трябваше да запишем дали да пишем тестове в нашата система за проследяване на проблеми, което създаде още една възможност за грешна комуникация.

Тези дискусии също бяха спорни в моменти, когато хората имаха различни нива на разбиране на целите на инженерството и продукта. Те често се превърнаха в заместител на по-широки разногласия относно стойността на качеството спрямо скоростта.

Липсата на предсказуемост с автоматизацията на тестовете също направи оценката по-трудна чрез въвеждане на допълнителна променлива. Тъй като автоматизацията на тестовете не беше задължителна, оценките на различни хора съдържаха различни имплицитни предположения относно обхвата на тестовата работа. Това допълнително усложни решенията, тъй като често бихме създавали изкуствен натиск от време за себе си, като не оценяваме времето за автоматизация на тестовете, което, както се досещате, доведе до по-малко тестове.

Този конкретен тип гъвкавост също създаде проблем с изоставането, защото позволи отлагането на работа. Ако поставите една задача върху изоставане, не е толкова трудно за управление и не изглежда като тежест. Въпреки това, извършването на това многократно във времето става по-скъпо по нелинеен начин, тъй като всяко преминаване през изоставането отнема все повече и повече време. Също така, колкото по-дълго задачите стоят натрупани, толкова по-вероятно е те да остареят. Всеки процес, който включва натрупване на работа по-бързо, отколкото е завършен, води до значителни дългосрочни разходи.

Незадължителното тестване допълнително доведе до лоши краткосрочни решения за коригиране на нашата инфраструктура за тестване. Тъй като автоматизацията на тестовете не беше необходима, щяхме да я избегнем, когато имаше сложен проблем с инфраструктурата за тестване. Така че тези важни проблеми наложиха данък върху всеки проект, който в крайна сметка надхвърли времето, което спестихме в дългосрочен план.

Когато се представи по този начин, изглежда очевидно, че правилното нещо, което трябва да направите, е да изисквате тестова автоматизация с всеки проект. Въпреки това ни отне много време, за да стигнем до този момент и да преодолеем опасенията за нарушаване на графика на проекта. Може да е трудно да се приемат определени забавяния на конкретни проекти за по-малко осезаема бъдеща полза.

За да идентифицирате компромисите с лоша последователност срещу гъвкавост във вашия екип и да се отървете от краткосрочното мислене, помислете за следните въпроси:

  • Какви решения хората прекарват най-много време в обсъждане многократно в различни контексти? Могат ли тези решения да бъдат заменени с прости насоки, които водят до един и същ отговор през повечето време?
  • Ако на отделни лица или екипи бяха дадени едни и същи задачи, какво биха направили по различен начин? Колко струва на другите тази липса на предвидимост?
  • Има ли места, където натрупвате натрупани корекции? Това може да е лоша краткосрочна оптимизация и може би е по-добре да направите тези неща веднага или веднага да решите, че не си струват.
  • Колко често хората правят изключения от насоките на вашия екип? Мъдри ли са тези изключения или водят до дългосрочни разходи, които надхвърлят краткосрочните ползи?
  • Конкретни хора носят ли отговорност за дългосрочните резултати от процесите и имат ли право да правят краткосрочни жертви, за да постигнат тези резултати?
  • Може ли всеки от вашия екип да обясни дългосрочните проблеми, с които процесите са предназначени да се борят? Има ли надежден начин новите хора да научат за тези проблеми? Ако не, тогава хората могат да направят лоши изключения от тези процеси.

Пренебрегване на съвкупните разходи и ползи

Друг начин, по който разумните местни решения могат да доведат до лоши глобални резултати, е когато има значителни разходи или ползи, които имат въздействие само в съвкупност. Тъй като нито едно решение не може да наклони везната, хората не вземат предвид съвкупните ефекти, когато вземат всяко решение.

Може би най-познатият общ ефект в софтуерното инженерство е фиксираната цена за използване на определена технология или метод, което допълнително усложнява повече, които са в действие.

Помислете например за езиците за програмиране. Всеки език изисква набиране на инженери, които знаят езика, установяване на програми за обучение, създаване на практики за разработка, закупуване на инструменти, управление на знания и т.н. Наличието на множество езици също прави по-трудно разпределянето на разработчици по проекти. Дори малко количество код на различен език може да доведе до големи глобални фиксирани разходи, а пълното премахване на език ще доведе до огромни спестявания.

Същият принцип важи и за използването на други технологии и практики, като различни библиотеки или рамки, операционни системи или дори процеси на гъвкаво планиране.

Поддържането на най-добрите практики е постоянна битка за инженерните екипи. Това е особено предизвикателство, когато имате голяма кодова база, което често прави непрактично мигрирането към нова технология наведнъж. Обичайният начин за справяне с това е постепенно преработване на кода, преместване на нещата част по част. Тъй като основното предимство на рефакторинга е намаляването на разходите за поддръжка, има смисъл първо да се даде приоритет на модулите, които изискват най-голяма поддръжка.

Проблемът с този инкрементален подход е, че можете да се окажете в ситуация, в която никога да не преработвате най-малко използваните модули. Ако разгледате един от тях изолирано, бъдещите спестявания може да не надвишават разходите за рефакторинг. Въпреки това, колкото по-малко стар код остава, толкова по-скъпа става всяка част за поддръжка, защото по-малко хора имат опит с него.

Освен това наличието на стар и нов код, работещи един до друг, драстично увеличава цялостната сложност. Ако старият код се задържи дълго време, публичната документация, поддръжката и разработчиците, които желаят и могат да работят по него, стават все по-оскъдни. Това създава още по-голяма дупка, която прави усилията за изкопаване по-малко привлекателни въпреки продължаващата тежест за поддръжка. Ако никой не погледне голямата картина, разумните местни решения могат да доведат до огромни глобални разходи и дълбоки съжаления.

Сблъскахме се с този проблем в няколко области на Collage.com, но най-скъпото беше пренебрегването на преработването на нашия код на фотокнига в продължение на пет години. През това време фотокнигите са били около 10% от приходите ни и имат едни от най-сложните потребителски интерфейси. Имахме по-добри възможности да подобряваме други продукти с по-малко усилия, така че продуктовите мениджъри решиха да не работят върху книги. Когато дойде време за решения за рефакторинг, ние избегнахме фотокниги, тъй като прогнозната полза нямаше да бъде много висока поради липса на бъдещи продуктови планове.

Първата ни грешка беше, че не успяхме да отчетем режийните разходи за тестване и коригиране на грешки, докато модифицирахме други области. Тъй като кодът на книгата беше сложен, зле структуриран и имаше ограничени регресионни тестове, той се разваляше през цялото време с несвързани промени. Това беше влошено от факта, че никой не беше запознат с кода, тъй като не го поддържахме актуален. Това беше особено болезнено за новите разработчици, които трябваше да отделят много време за тестване и отстраняване на грешки в стар код.

Въпреки че не добавяхме нова функционалност към нашия инструмент за създаване на книги в продължение на няколко години, актуализирането му в крайна сметка се превърна в приоритет. До този момент човекът, който ръководи проекта, никога не е работил върху кода, освен няколко корекции на грешки, а човекът, който първоначално го е написал (аз!), не беше на разположение да помогне. Големината на проблемите с този код го направи преработването му отнема много време.

Наред с годините на корекции на грешки и тестване, почистването на този код изискваше много повече усилия, отколкото би било необходимо, за да го поддържате актуален във времето. Всяко решение, което взехме, за да го избегнем, изглеждаше разумно само по себе си, но хлъзгавият наклон на техническия дълг ни струваше скъпо в крайна сметка.

Ето някои въпроси, които трябва да имате предвид, за да избегнете попадането в капана на пренебрегването на съвкупните ефекти:

  • Има ли лице, изрично отговорно за дългосрочните разходи за поддръжка на целия софтуерен пакет? Този човек има ли правомощията да приоритизира подобрения, които може да не са полезни за отделен екип, но са добри за компанията като цяло?
  • За кодови модули и системи, които нямате планове за актуализиране, каква е вероятността актуализирането им да стане приоритет в бъдеще спрямо шансовете те да бъдат изхвърлени изцяло?
  • Колко изолирани са модулите и системите, които изостават? Наистина ли имат ниски разходи за поддръжка или изискват чести корекции на грешки и тестване при извършване на промени в несвързан код?
  • Кои стари технологии, които все още се използват, ще станат по-трудни за работа с течение на времето поради липса на познания на разработчиците и обществена подкрепа? Кое би било най-проблемното, ако настоящите служители напуснат и трябва да наемете нови хора, които да работят върху тях?
  • Къде е присъствието на множество технологии или методи за една и съща цел, което увеличава цялостната сложност на кода?

Последна бележка

В тази статия се съсредоточихме върху местното срещу. глобална оптимизация и обхвана няколко начина, по които хората вземат грешни решения. Въпреки че се надяваме, че тези примери ще предотвратят определени грешки, те в никакъв случай не са изчерпателни.

В бъдещи статии се надявам да изследвам други сценарии, които карат интелигентните инженери да правят глупави неща. Ще се радвам да чуя отзиви за тези примери или предложения за бъдещи теми. Благодаря ви, че прочетохте!