Хабрахабр

[Перевод] Чему я научился у ведущего программиста

Год назад я начал работать на полную ставку в Bloomberg. И тогда же задумал написать эту статью. Я думал, что буду полон идей, которые смогу выплеснуть на бумагу, когда придёт время. Но уже через месяц понял, что всё будет не так просто: я уже начал забывать то, чему научился. Либо знания настолько хорошо усвоились, что мой разум заставил меня поверить, будто я всегда это знал, либо они просто вылетели у меня из головы.1

Каждый день, попадая в интересные ситуации, я описывал их. Это одна из причин, по которой я начал вести дневник. Я мог вблизи наблюдать за его работой, и видел, насколько она отличается от того, что сделал бы я. И всё благодаря тому, что я сидел рядом с ведущим программистом. Более того, в нашей команде не осуждается «подглядывание» за людьми, пишущими код. Мы много программировали вместе, что ещё больше облегчало мои наблюдения. Благодаря постоянным вставаниям я всегда был в курсе происходящего. Когда мне казалось, что происходит что-то интересное, я поворачивался и смотрел.

Вот чему я научился.
Я год просидел рядом с ведущим программистом.

Написание кода

Как называть вещи в коде

Одной из моих первых задач была работа над React UI. У нас был основной компонент, содержавший все остальные компоненты. Мне нравится добавлять в код немножко юмора, и я хотел назвать основной компонент GodComponent. Наступил момент ревью кода, и я понял, почему так трудно давать наименования.

Каждый кусок кода, который я окрестил, обрёл неявное предназначение. GodComponent? Это компонент, в который попадает вся дрянь, которую я не хочу помещать в нужное место. Он содержит всё. Назови я его LayoutComponent, и будущий я решил бы, что этот компонент присваивает макет. Что он не содержит состояния.

А в случае с названием GodComponent присутствие бизнес-логики не будет иметь значения. Ещё одним важным усвоенным мной уроком стало то, что если что-то выглядит слишком большим, вроде LayoutComponent с кучей бизнес-логики, то пора его рефакторить, потому что бизнес-логики здесь быть не должно.

Называть их в честь сервисов, которые на них работают, будет прекрасной идеей до тех пор, пока вы не запустите на этих кластерах что-то ещё. Нужно назвать кластеры? Мы дали им название в честь нашей команды.

doEverything() — ужасное название с многочисленными последствиями. То же самое относится и к функциям. Какой бы большой ни стала такая функция, вам это никогда не покажется слишком странным, ведь она же должна делать всё. Если функция делает всё, то будет чертовски сложно тестировать отдельные её части. Отрефакторьте. Так что поменяйте название.

Вдруг название будет слишком осмысленным и скроет какой-то нюанс? У осмысленного наименования есть и обратная сторона. Мне следовало прочесть документацию и предотвратить этот баг, подробнее об этом рассказано в разделе Байка. Например, закрытие сессий не закрывает подключение к базе данных при вызове session.close() в SQLAlchemy.

2 С этой точки зрения именование функций как x, y, z вместо count(), close(), insertIntoDB() не позволяет вкладывать в них определённый смысл и заставляет меня внимательно следить, что же делают эти функции.

Никогда не думал, что напишу про принципы наименования больше одной строки текста.

Унаследованный код и следующий разработчик

Бывало ли, что вы смотрите на код и он вам кажется странным? Почему так написали? Это же не имеет смысла.

Такой, знаете, с комментариями вроде «Раскомментировать код, когда Мухаммед разберётся в ситуации». Мне довелось поработать с унаследованной кодовой базой. Кто такой Мухаммед? Что вы тут делаете?

Отчасти решить эту проблему помогает ревью твоего кода коллегами. Я могу поменяться ролями и подумать о человеке, которому потом передадут мой код, покажется ли он ему странным? Это навело меня на мысль о контексте: нужно помнить о контексте, в котором работает моя команда.

Это же глупость… А, погодите, это я так сделал». Если я забуду этот код, вернусь к нему позднее и не смогу восстановить контекст, то скажу: «Какого хрена они так сделали?

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

Документация и комментарии в коде

Они помогают сохранить контекст и передать знания.
Как сказал Ли в How to Build Good Software: 

Главная ценность ПО не в созданном коде, а в знании, накопленном людьми, которые создали это ПО

У вас есть открытый для клиентов эндпойнт API, которым, похоже, никто ни разу не пользовался. Нужно ли его просто удалить? Вообще говоря, это технический долг. А если я скажу вам, что в одной из стран 10 журналистов раз в год отправляют свои отчёты на этот эндпойнт? Как это проверить? Если в документации об этом не упомянуто (так и было), то никак не проверить. Мы и не проверили. Удалили, а через несколько месяцев наступил тот самый ежегодный момент. Десять журналистов не смогли отправить свои важные отчёты, потому что эндпойнта больше не существовало. А люди, обладавшие знаниями о продукте, уже покинули команду. Конечно же, теперь в коде есть комментарии, объясняющие, для чего это нужно.

Причём с документацией не только по коду, но и по связанным с ним процессам. Насколько мне известно, каждая команда сражается с документацией.

Лично мне нравится, как Антирез разделил комментарии в коде по разным типам ценности. Мы ещё не придумали идеального решения.

Атомарные коммиты

Если вам нужно откатиться (а вам это понадобится. См. главу Тестирование), то будет ли этот коммит иметь смысл как единый модуль?

Как уверенно удалять паршивый код

Мне было очень неприятно удалять паршивый или устаревший код. Мне казалось, что всё написанное века назад является священным. Я думал: «Они же что-то имели в виду, когда так писали». Это противостояние между традицией и культурой с одной стороны, и мышлением в стиле «первичного принципа» с другой стороны. Это то же самое, что и в случае с удалением ежегодной-конечной-точки. Я усвоил особенный урок.3

Сотрите его. Я постарался бы обойти код, а ведущие разработчики постарались бы пройти сквозь него. Ага, стираем. Выражение if, к которому невозможно обратиться? Я просто написал поверх него свою функцию. А что сделал я? Во всяком случае, я только что увеличил сложность кода и переадресацию. Я не уменьшил технический долг. Следующему человеку будет ещё сложнее собрать кусочки картины воедино.

Сотри код, к которому не обратишься, и будь осторожен с кодом, который не понимаешь. Опытным путём я пришёл к заключению: есть код, который ты не понимаешь, а есть код, к которому ты точно никогда не обратишься.

Ревью кода

Ревью кода — прекрасный инструмент для самообразования. Это цикл внешней обратной связи, показывающий, как они написали бы код и как его написал ты. В чём разница? Один способ лучшего другого? Я спрашивал себя об этом при каждом ревью: «Почему они написали именно так?» И если не мог найти подходящий ответ, то шёл и спрашивал.

Это было какое-то безумие. Спустя первый месяц я начал находить ошибки в коде моих коллег (как они находили в моём). Ревью стало для меня гораздо интереснее, оно превратилось в игру, которой мне не хватало, игру, которая улучшала моё «чувство кода».

По моему опыту, не надо одобрять код, пока я не пойму, как он работает.


Моя GitHub-статистика.

Тестирование

Я так полюбил тестирование, что мне неприятно писать код в кодовой базе без тестов. 

4 Именно так я и делал. Если ваше приложение делает лишь что-то одно (как все мои школьные проекты), тогда всё ещё можно тестировать вручную. Я не хочу тратить полчаса на тестирование, и иногда что-то упускаю из виду. Но что происходит, если приложение выполняет 100 разных задач? Кошмар.

Здесь помогают тесты и автоматизация тестирования.

Это документация моих представлений о коде. Я отношусь к тестированию как к документации. Тесты говорят мне, как я (или кто-нибудь до меня) представляю работу кода и где что-то ожидаемо должно пойти не так. 

Сегодня, когда я пишу тесты, я стараюсь:

  1. Показать, как использовать тестируемый класс, функцию или систему.
  2. Показать, что, по моему мнению, может пойти не так.

Как следствие, чаще всего я тестирую поведение, а не реализацию (вот пример, который я выбрал на перерывах в Google).

В пункте 2 я не упомянул об источниках багов.

Это ещё одна из причин, почему что-то может пойти не так. Когда я замечаю баг, то убеждаюсь, что у исправления есть соответствующий тест (это называется регрессионным тестированием) для документирования информации. 5

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

Так выглядит общая ситуация с тестированием.

Я говорю о средах развёртывания. Но это не единственная разновидность тестирования, которую я применяю. У вас могут быть идеальные модульные тесты, но если у вас нет тестов системных, то может произойти нечто подобное:

Это относится и к хорошо протестированному коду: если у вас на компьютере нет необходимых библиотек, то всё обрушится.

  • Есть машины, на которых вы разрабатываете (источник всех мемов вроде «На моём компьютере работало!»).
  • Есть машины, на которых вы тестируете (могут совпадать с предыдущими).
  • Наконец, есть машины, на которых вы развёртываете (они не должны совпадать с машинами, на которых вы разрабатывали).

Если на машинах тестирования и развёртывания среды не совпадают, у вас будут проблемы. А избежать этого помогут среды развёртывания.

Мы ведём локальную разработку в Docker на своем компьютере.

Здесь его можно протестировать со всеми необходимыми системами. У нас есть среда разработки, эти компьютеры оснащены набором библиотек (и инструментов разработки), и сюда мы устанавливаем написанный код. Наконец, у нас есть эксплуатационная среда — машины, на которых исполняется код для наших клиентов. Также у нас есть бета/стейджинговая среда, которая полностью повторяет эксплуатационную среду.

Например, разницу API у запрашивающей и отвечающей системы. Идея заключается в том, чтобы выловить ошибки, которые не всплыли в ходе модульного и системного тестирования. Не у всех есть возможность создать собственную инфраструктуру. Думаю, в случае с личным проектом или маленькой компание ситуация может быть совсем иной. Однако можно прибегнуть к услугам облачных сервисов, например, AWS и Azure.

AWS ECS использует для развёртывания Docker-образы, так что процессы в разных средах будут относительно согласованы. Вы можете настроить отдельные кластеры для разработки и эксплуатации. Вы вызываете правильную конечную точку из правильной среды? Есть нюансы с точки зрения интеграции между разными AWS-сервисами.

Это ускоряет цикл обратной связи. Можно пойти ещё дальше: скачать альтернативные контейнерные образы для других AWS-сервисов и настроить локальную полнофункциональную среду на основе Docker-Compose. 6 Возможно, я наберусь больше опыта, когда создам и запущу свой побочный проект.

Снижение рисков

Какие шаги вы можете предпринять, чтобы снизить риск катастрофы? Если речь идёт о новом радикальном изменении, то как можно удостовериться в минимальной длительности простоя, если что-то пойдёт не так? «Нам не нужно полностью развёртывать систему из-за всех этих новых изменений». Что, правда? И почему я об этом не подумал!

Архитектура

Почему я говорю об архитектуре после написания кода и тестирования? Её можно поставить и первой, но если бы я не программировал и не тестировал в используемой мною среде, то, вероятно, не преуспел бы в создании архитектуры, учитывающей особенности этой среды.7

Нужно очень многое продумать при создании архитектуры.

  • Как будут использоваться числа?
  • Сколько будет пользователей? Насколько может увеличиться их количество (от этого зависит количество строк в базе данных)?
  • Какие подводные камни могут встретиться?

Мне нужно превратить это в чеклист под названием «Сбор требований». Сейчас у меня пока недостаточно опыта, постараюсь сделать это в следующем году в Bloomberg. Этот процесс во многом противоречит Agile: сколько можно проектировать архитектуру, прежде чем переходить к реализации? Всё дело в балансе, нужно выбирать, когда и что вы будете делать. Когда имеет смысл ринуться вперёд, а когда — отступить назад? Конечно, сбор требований не равносилен обдумыванию всех вопросов. Думаю, это окупается, если включить в проектирование ещё и процессы разработки. Например:

  • Как будет протекать локальная разработка?
  • Как мы будем упаковывать и развёртывать?
  • Как мы будем проводить сквозное тестирование?
  • Как мы будем проводить стресс-тестирование нового сервиса?
  • Как мы будем хранить секреты?
  • CI/CD-интеграция?

Недавно мы разработали новый поисковый движок для BNEF. Работать над ним было чудесно, я организовал локальную разработку и узнал о DPG (пакетах и их деплое), поборов деплой секретов. 

Кто бы мог подумать что деплоить секреты в прод может быть таким нетривиальным:

  1. Их нельзя поместить в код, ведь кто-то может их заметить
  2. Хранить их как переменную окружения как предлагает спека 12 факторов приложения? Неплохая идея, но как их туда положить? (Заходить на прод чтобы заполнить переменные окружения каждый раз, когда стартует машина — боль)
  3. Деплоить их как файлы? Но откуда они возьмутся и как их заполнять?

Мы не хотим всё делать вручную.

Наш код получает от базы секреты при запуске. В результате мы пришли к базе данных с управлением доступом на основе ролей (только мы и наши компьютеры можем общаться с базой данных). Этот подход отлично реплицируется в рамках сред разработки, стейджинга и эксплуатации, секреты хранятся в соответствующих базах данных.

Вам не нужно как-то заботиться о секретах. Опять же, с облачными сервисами вроде AWS ситуация может быть совершенно иной. Это сильно всё упрощает, но я рад, что получил опыт, благодаря которому могу оценить эту простоту. Получите аккаунт для своей роли, введите секреты в интерфейсе, и ваш код найдёт их, когда они понадобятся.

Создаём архитектуру, не забывая о сопровождении

Проектирование систем вдохновляет. А сопровождение? Не слишком. Моё путешествие по миру сопровождения привело меня к вопросу: почему и как деградируют системы? Первая часть ответа связана не с выводом из эксплуатации всего устаревшего, а только с добавлением нового. Склонность добавлять, а не удалять (ничего не напоминает?). Вторая часть — это проектирование с мыслью о конечной цели. Система, которая со временем начинает делать то, для чего не предназначалась, не обязательно будет работать так же хорошо, как система, изначально спроектированная для тех же задач. Это подход в стиле «отступить на шаг назад», а не хитрости и уловки.

Я знаю не меньше трёх способов снижения скорости деградации.

  1. Разделяйте бизнес-логику и инфраструктуру: обычно деградирует инфраструктура — растёт нагрузка, устаревают фреймворки, проявляются уязвимости нулевого дня и т.д.
  2. Создавайте процессы с учётом будущего сопровождения. Применяйте одинаковые обновления для старых и новых битов. Это предотвратит появление различий между старым и новым и сохранит весь код в «современном» состоянии.
  3. Убедитесь в том, что выбрасываете всё ненужное и старое.

Развёртывание

Буду ли я упаковывать фичи вместе или развёртывать их по одной? В зависимости от текущего процесса, если вы будете упаковывать их вместе, то жди беды. Спросите себя, почему вы хотите упаковывать фичи вместе?

  • Развёртывание занимает много времени?
  • Ревью кода проходит не слишком весело?

Какой бы ни была причина, эту ситуацию нужно исправлять. Я знаю как минимум две проблемы, связанные с упаковыванием:

  1. Вы сами блокируете все фичи, если в одной из них есть баг.
  2. Вы повышаете риск возникновения проблем.

Какой бы процесс развёртывания вы ни выбрали, вам всегда хочется, чтобы ваши машины были как скот, а не как домашние животные. Они не уникальны. Вы точно знаете, что исполняется на каждой машине как воссоздать их в случае смерти. Вы не расстроитесь, если какая-нибудь машина умрёт, вы просто поднимаете новую. Вы их пасёте, а не растите.

Когда что-то идёт не так

На тот случай, если что-то пойдёт не так — а оно пойдёт, — есть золотое правило: минимизировать влияние на клиентов. В случае сбоев моим первым желанием всегда было заняться исправлением. Похоже, это не оптимальное решение. Вместо того, чтобы заниматься исправлением, даже если это можно сделать одной строкой, сначала нужно откатиться. Вернитесь к предыдущему рабочему состоянию. Это самый быстрый способ вернуть клиентов к работающей версии. Только потом я выясняю, в чём проблема, и исправляю. 

Я нахожу странным, насколько моё естественное желание и инстинкты противоречат оптимальному решению. То же самое применимо и к «испорченной» машине в вашем кластере: выключите её, пометьте как недоступную, прежде чем выяснять, что с ней произошло.

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

Что именно я могу подтвердить, имея текущие ресурсы? С тех пор я научился искать «сначала в ширину», а потому уже «сначала в глубину», чтобы исключить верхнеуровневые причины.

  • Машина работает?
  • Код установлен верно?
  • Конфигурация присутствует?
  • <Характерная для кода конфигурация>, вроде того, корректно ли прописана маршрутизация?
  • Правильная ли версия схемы?
  • А потом уже погружаюсь в код.

Мы думали, что был неправильно установлен nginx, но оказалось, просто конфигурация была отключена

Конечно, мне не нужно делать это каждый раз. Иногда достаточно лишь сообщения об ошибке, чтобы сразу заняться разбором кода. Когда я не могу определить причину, я стараюсь свести к минимуму количество изменений-в-коде-ради-того-чтобы-найти-причину. Чем меньше изменений, тем быстрее я смогу найти настоящий корень проблемы. Кроме того, теперь у меня есть памятка для багов, которая сэкономила мне больше часа на размышления «что я упустил?» Иногда я забываю о простейших проверках, вроде настройки маршрутизации, соответствия версий схемы и сервиса, и т.д. Это ещё один шаг по освоению стека технологий, который я использую, и то, что обретаешь лишь с опытом — интуицию в определении, что же именно не работает.

Байка

Эта статья не может быть полной без байки. Мне нравится их читать, и хочу одной из них поделиться с вами. Это история о поиске и SQLAlchemy. В BNEF работает много аналитиков, которые пишут отчёты об исследованиях. При публикации отчёта мы получаем сообщение. При получении сообщения мы обращаемся к базе данных через SQLAlchemy, получаем необходимые данные, преобразуем и отправляем на индексирование в экземпляр Solr. И как-то возник странный баг.

Машины включаются в течение дня, так что это было первое, что я проверил. Каждое утро подключение к базе приводило к сбою с ошибкой «MYSQL server has gone away.» Иногда это случалось и днём. Мы делали тысячи запросов к базе в течение дня, всё было в порядке. Нет, при включении компьютера ошибка не возникала. Так в чём же дело, что приводило к сбою?

А если сессия та же, и спустя длительное время приходит запрос, то мы пропускаем таймаут и сервер исчезает. Может быть, мы не закрывали сессии после транзакций? Быстро просмотрели код и убедились, что мы используем диспетчер контекста для каждой операции чтения, в ходе которой применительно к __exit__() вызывается session.close().

На следующее утро я пришёл на работу и снова столкнулся с этой ошибкой. Целый день мы проверяли всё, что можно, и ничего не обнаружили. Были все симптомы неправильного закрытия сессии. А секунду спустя три других запроса к индексу прошли успешно. Конец истории вам известен.

Это решило проблему. Session.close() в MySQL-диалекте SQLAlchemy не закрывает подключение к базе данных, пока если не используется NullPool. И отсюда проистекает ещё один урок: в большинстве ответов на StackOverflow (конечно, я там искал!) советовали настроить длительность таймаута сессии, или настроить параметр, управляющий объёмом пересылаемых в SQL-выражении данных. Забавно, что этот баг возник лишь потому, что мы не публиковали отчёты об исследованиях вечером или в обед. Я проверил, что размер запроса у нас не превышает ограничения, а сессии мы закрывали (хаха), так что таймаут просто отсутствовал.  Всё это не имело для меня смысла, поскольку не было связано с истинной причиной.

Нам казалось бы, что проблема решена, до первого выходного в течение недели — и тогда первый отчёт на следующее утро завершился бы сбоем. Мы могли бы «исправить» этот баг, увеличив длительность таймаута сессии с 1 часа до 8.

Это балансирование между настройкой параметров, игрой со статистикой и исправлением причины.

Мониторинг

Это то, о чём раньше я никогда не думал. Честно говоря, пока я не начал работать программистом на полную ставку, я никогда не занимался поддержкой систем. Я лишь создавал их, использовал с неделю и шёл дальше. 

Я не могу исправить баги, если не знаю об их существовании. Поработав с двумя системами, одна из которых обладала замечательным мониторингом, а другая не могла этим похвастаться, я начал очень высоко ценить мониторинг. «Чем я занимаюсь?! Хуже всего, когда узнаёшь о багах от клиентов. Я даже не знаю о проблемах в системе, которой владею?».

Журналирование в коде, как и дневник, процесс эволюционный. Я считаю, что мониторинг складывается из трёх компонентов: журналирования, метрик и оповещений. Со временем находите несколько багов, для исправления которых у вас мало информации. Вы прикидываете, что вам нужно будет мониторить, начинаете журналировать и запускаете систему. Думаю, вы интуитивно понимаете, что важно журналировать. Пришло время расширить журналирование — чего не хватает вашему коду? Я считал, что достаточно будет логов запросов-ответов, а он фиксировал кучу метрик, вроде длительности исполнения запроса, некоторые внутренние вызовы, сделанные кодом, и т.д. Я и тот ведущий программист журналировали очень разные наборы данных. А при ротации логов ещё и сортировал статистику.

Если вы не знаете, в каком состоянии была система, как вы можете её воссоздать? Практически невозможно заниматься отладкой без логов. Вы сами определяете набор метрик и отправляете информацию по мере исполнения кода. Метрики можно извлекать из логов или выделять в коде (например, отправку событий в AWS CloudWatch и Grafana).

Если одной из метрик является количество серверов, работающих в данный момент в эксплуатации, то когда её значение падает до 50 %, это должно быть поднимать настоящую тревогу — возникла серьёзная проблема. Оповещения соединяют все элементы в замечательную систему мониторинга. Ещё одно оповещение. Количество сбоев превысило порог? Я крепко сплю по ночам, потому что знаю — в случае чего меня разбудят (погоди, что?).

Когда исправляешь баги, то думаешь не только о том, как исправить, но и о том, почему ты не определил их раньше? Это приводит к ещё одной привычке в разработке. Как можно улучшить мониторинг, чтобы предотвращать такие проблемы? Было ли оповещение?

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

Заключение

Я многому научился за последний год. Я рад, что решил написать эту статью, с её помощью мне удалось полнее оценить, насколько я вырос как специалист. И я надеюсь, что вы узнали для себя что-то полезное!

Посмотрим, к чему это приведёт! Сейчас я сижу рядом с двумя ведущими разработчиками.

Это приводит к мультипликативному эффекту, позволяя их коллегам опираться на их работу и решать свои задачи гораздо быстрее и надёжнее — How to Build Good Software. Хорошие инженеры самостоятельно проектируют системы, которые получаются более надёжными и лёгкими для понимания.

В чём я не уверен

Я ещё не познал всех тайн программного инжиниринга. Так что эта глава служит мне напоминанием: я ещё многого не знаю! Если я всё делаю правильно, то в следующем году этот список должен стать длиннее.

  1. Мыслить абстракциями или реализациями?
  2. Должен ли я иметь твёрдое мнение относительно способов решения задач? Например, как следствие набитых шишек? Достаточно ли я сделал, чтобы иметь мнение?
  3. Продумывание рабочих процессов. Если нужно срочно или ситуативно поменять подход, то можно ли считать процесс нарушенным? Нужно ли его исправлять?
  4. Являются ли вспомогательные классы (utils) (папка, в которую ты складываешь то, что не знаешь, куда деть) признаком того, что код «с душком»?
  5. Как работать с документацией по коду и рабочим процессам?
  6. Как мониторить интерфейс так, чтобы замечать, когда что-то выглядит неправильно?
  7. Что лучше — потратить время на проектирование идеального API или контракта в коде, или хакинг и многократное итерирование в поисках лучшего решения?
  8. Легкий способ или правильный? Не уверен, что правильный способ всегда лучше.
  9. Делать самостоятельно или показывать тем, кто не знает, как сделать. Первое быстрее, а второе означает, что вам редко придётся делать это самостоятельно.
  10. Когда рефакторишь и избегаешь больших PR: «Если бы я сначала изменил все тесты, то я бы увидел, что у меня изменилось 52 файла, и это, очевидно, слишком много, но я сначала занялся кодом, а не тестами». Стоит ли оно того?
  11. Дальнейшее исследование снижения рисков. Какие существуют стратегии для уменьшения рисков в проектах?
  12. Эффективные способы сбора требований?
  13. Как снизить скорость деградации системы?

Примечания

  1. Такое происходит со многими знаниями. Вы знаете, как ездить на велосипеде? Можете кого-нибудь научить? Опишете им конкретные этапы, как вы это делали?
  2. Я имею в виду, что нужно не писать код с именами x(), y() и z(), а думать о них как о x(), y() и z(). Не думайте, что это WYSIATI.
  3. Классический барьер Честертона.
  4. Но я больше так не поступил бы. Однажды перейдя на сторону автоматизированных тестов, обратного пути нет?
  5. Есть мнение, что ситуация выходит из-под контроля, если провести миллион тестов на всё, что может пойти не так. Судя по тому, это не верно.
  6. Я давно этого не делал, так что не знаю, насколько легко найти или создать конкретные Docker-образы для AWS.
  7. Здесь в качестве среды может выступать ваш стек технологий.
Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть