Мои правила
Можно считать эту статью второй частью в неком цикле про мои принципы в работе. В первой части я писал про неважные для меня вещи на примере code-style. Теперь логично рассказать о том, что для меня важно. Про некоторые правила, которые помогают мне в работе. Это не только про написание кода, но и про процесс разработки в целом.
Я начну с менее специфичных вещей, а закончу тем, что относится к миру .net C#.
Прозрачность работы
Делаю свою работу прозрачной. Это значит, что она связана с каким-то тикетом в YouTrack или другой системе, в которой ведутся задачи. В задаче должны быть причины, описание и ожидаемый результат. Всегда можно понять, над чем я работаю, работал. Даже если это запрос со стороны, инцидент на боевой или забытый тест к какой-то задаче.
Это дисциплинирует и позволяет расставлять приоритеты. А вот это обращение важнее того, чем я сейчас занимаюсь? В большом количестве случаев оказывается, что можно создать задачу, отложить её до ближайшего планирования и сделать в рабочем порядке.
Такой вот я бюрократ.

Связь кода и задач
Следствие предыдущего правила. Если вся моя работа отражена в задачах, то целесообразно связывать её с кодом. То есть просто коммиты в мастер без связи с задачей не допускаются.
Множество раз я попадал в ситуацию, когда не понимал суть какого-то условия в коде, открывал историю, а там сиротливый комментарий «add some checks» от уволившегося разработчика:

Поэтому я каждый коммит в master сопровождаю ссылкой на задачу:

И вот уже глядя на историю, мы можем найти нужную задачу. А в ней — мотивацию, причины, связи с другими задачами. Это многократно упрощает расследование проблем и поиск ответов в истории.
История изменений
Во время решения задачи я могу несколько раз поменять реализацию. Сначала сделал A, потом поменял A на B, потом после ревью B на C, а после тестирования еще C на D. Но в итоге в мастере мне не нужна вся цепочка A → B → C → D, я хочу видеть только конечный результата A → D без промежуточных метаний.
Поэтому, я не оставляю в мастере цепочки промежуточных коммитов:

Все эти фиксы фиксов мне не нужны. Они могут остаться в ветке и там они даже будут полезны, а в мастер попадет один коммит:

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

Думаю, уже здесь вы со мной не согласны, но это мои правила, имею право.
Такая организация работы с задачами и историей изменений потом позволяет относительно быстро собрать результаты своей работы за какой-то период (что будет полезно при оценке результатов работы), при этом не погружаясь в мелкие детали конкретного коммита.
Бесполезное удаляй
Постоянно падают интеграционные тесты — удаляю. Не работают графики или показывают ерунду — удаляю. Пишем ошибки в логи, но ничего не делаем с ними — значит, это не ошибки. Удаляю.
Всё это создаёт бесполезный фоновый шум, который ест ресурсы, время, внимание. Падающие тесты постоянно надо чинить, графики создают ложную уверенность, куча логов скрывают реальные проблемы. Если когда-то вам это опять понадобится — всё есть в истории репозитория.
Это же правило можно применить не только к коду, но и ко всему процессу разработки. Периодически стоит пересматривать все процессы на предмет актуальности текущей ситуации.
Постоянно улучшай
Я не верю в задачи на рефакторинг. Особенно, если это задачи вида «Отрефакторить класс HugeMegaClassWithLogic», то, скорее всего, они так и будут лежать в бэклоге до момента, когда система не придет в состояние, где ни одна новая задача не может быть сделана без этого рефакторинга.
Поэтому включаю рефакторинг в свою постоянную работу. Делаю его перед основной задачей, смотрю на код вокруг в процессе решения задачи. Сотня небольших правок могут сделать больше уже сейчас, чем одна задача, которая будет сделана когда-то через 3 года. Да, это может увеличить задачу, но немного. Да, можно облажаться, но облажаться можно всегда — это надо принять и уметь с этим работать. Со временем вырабатывается навык и понимание, какой объем рефакторинга стоит добавить в задачу, чтобы это не повлияло значимо на сроки и объем изменений оставался контролируемым.
Несколько релизов лучше одного
Три небольших последовательных релиза лучше одного большого. Меньше изменений, меньше хаоса, больше контроля. Всегда стараюсь делать обратно совместимые изменения, чтобы релиз прошел бесшовно для клиентов (даже если это наши клиенты или наш сервис). Лучше сделаю лишний релиз и почищу костыли, чем буду делать один синхронный релиз нескольких систем. Так больше возможностей откатить неудачные изменения.
Неопределенность
Наличие какой-то неопределенности в задаче — это нормально. В больших задачах практически невозможно учесть все негативные сценарии и потенциальные проблемы. В попытках найти ответы на все вопросы и принять все решения до старта можно бесконечно долго отодвигать тот самый старт. Скорее всего, можно начать с небольшого понятного шага, продвигаться постепенно, снимая неопределенность. Конечно, в этом тоже есть риск, и его нужно учитывать.
Признавай ошибки
Не каждое решение будет удачным. В чем-то я не эксперт. Я могу что-то делать плохо. Важно это понимать. Важно останавливаться, когда что-то идет не так. Если какое-то решение становится слишком сложным, хрупким, то я не боюсь его полностью откатить. Это будет лучше, чем бесконечно исправлять последствия таких решений. Неудачные решения лучше всего конвертировать в свой опыт.
Метрики
Хочешь понимать, как работает приложение — пиши метрики. Логи помогают разобраться в конкретной ситуации, а метрики дают общее представление о работе приложения. Сколько операций выполнено, сколько данных получено, среднее (шучу, среднее никто не использует) перцентили времени выполнения — всё это наглядно отображает работоспособность приложения.

Тесты
Держу тесты рядом с кодом. Для конкретной сборки есть рядом сборка с тестами:

Никаких общих мега-сборок со всеми тестами. Опять же, меньше связность, больше сплоченность. Потрогал сборку — запустил тесты рядом.
Больше пишу unit-тесты. Если сложно написать тест на класс или метод, значит он плохо спроектирован: много зависимостей, большая сложность. Это повод задуматься и переписать.
Batch-API
Проектируя API закладываю возможности пакетной обработки данных. Будь то запросы на получение или обновление данных. Скорее всего, это пригодится. Проблема N+1 запроса существует на всех уровнях и лучше заранее предусмотреть возможность пакетной обработки также на всех уровнях: клиент, API, база данных. Клиенты точно скажут вам спасибо. А еще получите значительную экономию ресурсов.
MyCoolControllerBase
Не делаю никаких базовых классов для приложений, контроллеров. Никаких ProjectApplicationBase или ProjectControllerBase. Не делаю одни проекты на основе других. Считаю, что у меня достаточно базовых кирпичиков, из которых можно собрать любое приложение и задеплоить его во внутреннее облако. Это можно сделать буквально за день.
Создавая такие базовые классы, вы создаете свою уникальную инфраструктуру, к которой нужно будет адаптироваться новичкам. А старички перестают понимать, как работать без неё, что осложняет переход между проектами.
Вы можете возразить, что там много общего кода. Но в общей массе бизнесового кода это незначительная доля. И меняться он будет крайне редко. Отсутствие такой сильной связности позволит вам вносить важные структурные изменения по отдельности, а не везде разом. Так что придерживаюсь правила: дублирование лучше сильной связности.
IEntity
При проектировании БД в Code First подходе велик соблазн создать еще какую-то базовую сущность для всех объектов, добавить туда обязательный идентификатор типа guid (или любой другой)… Потом можно построить на этом свои репозитории с использованием какого-то BaseRepository
Так таблицы обрастают суррогатными ключами, дополнительными индексами, от которых потом сложно избавиться. Поэтому более внимательно отношусь к проектированию таблиц в БД. Где-то может быть и составной первичный ключ, который не укладывается в концепцию с общим IEntity. Лучше без него.
Контейнеры
Под конец сместимся в еще более специфичные вещи.
Предпочитаю явно регистрировать все зависимости в контейнере. Опять же дело в уровне контроля за жизненным циклом объектов. Но такое решение нужно принимать где-то вначале разработки. В проектах с историей, где уже есть авторегистрация, изменить это будет уже сложно. Скорее всего, и не целесообразно.
Но в подобных же проектах я встречал больше всего проблем с работой контейнера: создание лишних экземпляров и как следствие утечки памяти, неочевидные состояния объектов.
Зачем нужны правила?
Тем более правила, которые я сам себе поставил.
Это может выглядеть как искусственные ограничения, но я воспринимаю их как набор понятных практик, которые работают в 95% случаев. Придерживаясь подобных правил, я могу получать предсказуемый результат. Это особенно хорошо работает на старте новых проектов. Я могу заложить ту основу, которая будет будет достаточно расширяемой и понятной через пару лет.
Всегда ли эти правила нужны? Конечно же нет. Всегда ли они работают? Тоже нет. Есть команды и проекты с другими процессами, подходами. Если мои правила не противоречат существующим, то начинаю их внедрять в своих задачах, показывая личным примером, какую можно получить пользу. Где-то придерживаюсь существующих подходов, где-то предлагаю изменения.
Не стоит забывать, что правила не являются самоцелью. Если для достижения результата здесь и сейчас нужно их нарушить, то так и быть. Быстрый фикс горящего прода, проверка какой-то гипотезы, появление новых инструментов — всё это повод срезать угол и сделать так, как нужно в данный момент.
Это мои правила. Наверняка, вы с чем-то не согласны. А, быть может, о чем-то не задумывались. Тогда мне кажется, что это хороший повод посмотреть вокруг и попытаться сформировать свои.