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

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

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

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

1. Намалете техническия дълг

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

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

Вашият код и/или модел на данни може да ви лъже — това създава ненужна сложност и объркване и често води до грешки (бъгове). Дори по-лошо, това може да се превърне в порочен кръг, в който инженерите са принудени да предприемат допълнителни преки пътища, за да спазят крайните срокове.

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

2. Бъдете прости

Когато НАСА проектира космически кораб, обикновено има два основни подхода за осигуряване на надеждност. Единият е излишък, т.е. резервни компютри, резервно захранване и т.н. Когато резервирането не е възможно, те използват простота.

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

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

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

Всъщност може да се твърди, че излишъкът също идва със свои собствени недостатъци, тъй като добавя сложност и възможни SPOF (единична точка на повреда).

3. Направете своя код четим

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

„Има два начина за конструиране на софтуерен дизайн: единият начин е да го направите толкова прост, че очевидно да няма недостатъци, а другият е да го направите толкова сложен, че да няма очевидни недостатъци.“ — C. A. R. Hoare

Според мен най-добрите програмисти са тези, които могат да приемат сложно поведение и да го разбият на сравнително прост, четим, разумен и комуникативен код, който повечето програмисти могат да разберат. Писането на четим код само по себе си е изкуство и умение, чието усвояване отнема години.

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

Една от любимите ми беседи по темата е „Седемте неефективни навика за кодиране на много програмисти“ от Кевлин Хени.

4. Следвайте стратегия за управление на инциденти

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

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

Ако вашите грешки нямат голямо усилване на сигнала, тяхната стойност е значително намалена.

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

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

Няколко предложения:

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

Подобрете сигнала си и затворете веригата.

5. Следвайте най-добрите практики за обработка на грешки

Без значение от вашата стратегия, доброто управление на инциденти започва на ниво приложение (програмистът). Помнете аксиомата „Боклук вътре, боклук вън“.

„Почти всички катастрофални повреди са резултат от неправилно обработване на нефатални грешки, изрично сигнализирани в софтуера.“ (https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf)

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

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

Бъдете много внимателни да преглъщате неочаквани грешки. Грешките, които не се обработват от незабавния код, трябва винаги да се разпространяват надолу по стека и всеки слой трябва да обработва само грешки, с които знае какво да прави. В крайна сметка, ако дадена грешка стигне чак до глобален „обхватен“ манипулатор на грешки, тя трябва да се третира по възможно най-елегантния начин и да се превърне в инцидент(вижте # 4).

Не правете това:

catch(e){ 
  // this won't happen
}

Защото да, ще стане.

Горещо препоръчвам тази реч, изнесена от Луис Елис. Въпреки че е пригоден за приложения на Node, неговият съвет се превежда добре на други платформи и е много лесен за следване.

6. Използвайте безопасен за тип език за програмиране

„Типовете обезсилват повечето от глупавите грешки, които могат да се промъкнат в кодовите бази, и създават бърза обратна връзка, за да поправят всички малки грешки при писане на нов код и рефакторинг.“ (https://serokell.io/blog/why-typescript)

Javascript е като игра с кибрит. Не мога да ви кажа колко пъти Typescript ми е спасявал задника през последните няколко години. „Строгите езици“ извеждат грешка, когато неочаквано смесвате типове. Статично въведените езици, като Typescript, Java и C#, ще направят това по време на времето на компилиране и ще изведат грешка дори преди да внедрите своя код. Това е страхотно, защото може да открие проблеми дори преди да се случат, точно във вашата IDE. Статичното писане е едно от нещата, които ми липсваха най-много в програмирането в Java. Днес с Typescript ние дори получаваме поддръжка за фантастични неща като generics!

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

7. Кодирайте с GUTs (добри тестове на единица)

„Мисля, че когато чуете фразата „това е просто тестов код“. За мен това мирише на код.“— „Алън Пейдж“

Единичните тестове осигуряват „зелена светлина“, която казва на програмистите, че не са счупили нищо. Умните програмисти често провеждат модулни тестове по време на процеса на разработка, особено по време на рефактор. Без тази зелена светлина може да бъде обезсърчително да се направят тези рефактори, които са необходими за предотвратяване на натрупването на технически дълг (виж #1).

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

Ето няколко предложения:

  • Използвайте функционално програмиране и многослойна архитектура, за да сведете до минимум зависимостите за макетиране. Чистите функции са много по-лесни за писане на модулни тестове. Помислете за разделяне на приложните слоеве, като например с „шестоъгълна архитектура“. По този начин можете да избутате трудния за единица тест код до краищата на вашето приложение.
  • Тествайте поведение, а не внедряване. Това е известно разработка, управлявана от поведението (BDD). Не е необходимо да тествате всяка вътрешна функция директно, за да достигнете 100% покритие.
  • Опростете твърденията си. Не е необходимо да научавате нов език и да пишете новела, за да твърдите, че две неща са равни. Всъщност някои предполагат, че „едно твърдение е всичко, от което наистина се нуждаете“.
  • Разбийте тестовете си на секции. Харесвам модела „дадено, кога, тогава“. Вашият тестов код трябва да разказва история.
  • Бъдете прости. Не се опитвайте да правите твърде много в един тест. Малко повторение е добре. Предпочитам да видя някакво повторение, отколкото тест, който продължава стотици редове.

8. Използвайте инструменти за тестване на интеграция от край до край

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

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

9. Направете ръчно тестване

„Откриването на неочакваното е по-важно от потвърждаването на известното.“ — Джордж Е. П. Бокс

Дори ако трябваше да имате 100% покритие на единица тест и обширно покритие от автоматизиран тест за интеграция от край до край, все пак бих препоръчал да направите поне някои основни ръчни тестове за дим – както в среда за разработка, така и в производствена среда – съсредоточавайки се около областите на въздействие.

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

Докато сме на темата, винаги трябва да тествате в производство след стартирането. „Но защо да тествате в „производство“, когато вече сте тествали в „постановка“?“, питате вие. „Инсценирането не е ли същото като продукцията?“. Не точно. Те са различни среди, изпълняващи различни конфигурации, сочещи към различни физически бази данни (дори ако имат идентични данни) и вероятно с куп други фини разлики също (т.е. компресия в предния край, регистриране, режим на отстраняване на грешки, сигурност и т.н. ). Преди да стартирате vim и да започнете да пишете производствени тестове, трябва да ви предупредя относно провеждането на автоматизирани тестове в производството. Грешки могат да възникнат дори във вашите тестове, така че се уверете, че не са разрушителни или имат ограничени възможности.

Между другото, ръчното тестване не трябва да бъде изцяло тежест на инженерите или вашия QA отдел. Можете да възложите това! Има много компании и независими професионалисти по целия свят, които могат да направят ръчно тестване за вас на много достъпна цена, 24/7. Всъщност работих за лош стартъп (Advizr), където възложихме това ръчно тестване вместо автоматизирани тестове за интеграция (случайно се получи добре за нас).

10. Учете се от грешките си

„Провалът не е фатален, но неуспехът да се промениш може да е фатален.“ — Джон Уудън

Ако се учите от грешките си, не само предотвратявате повторна грешка, но и ще научите как да предотвратите подобни. Всъщност понякога можете да извадите истински късмет и малка грешка да ви отведе по пътя към намирането на критичен дефект в дизайна на вашата система. Лично съм го виждал да се случва няколко пъти. Това е, което се нарича „провал напред“.

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

Марк Х. Уайнър е ветеран в стартиращ бизнес с два успешни излизания и специален интерес към мащабиране и критични за безопасността системи. Той е съосновател на NextEMR, едно от първите уеб базирани приложения за електронно медицинско досие. Бил е и водещ инженер по „софтуер за клинични изпитвания“, „Marvel.com“ и инфраструктура в „Shutterstock“.