Обновить
118.97

Качество кода *

Как Макконнелл завещал

Сначала показывать
Порог рейтинга
Теги:
-7
Комментарии0

Мы иногда во внутреннем чате обмениваемся фрагментами кода с неочевидными ошибками, которые обнаруживаются с помощью PVS-Studio в каком-нибудь открытом проекте. Мол, кто быстро сообразит, что не так с кодом?

Вчера коллега поделился вот таким фрагментом кода из проекта SereneDB:

template <typename T>
struct NumericParameter : public Parameter {
  using ValueType = T;
  ....
  std::string name() const override {
    if constexpr (std::is_same_v<ValueType, int16_t>) {
      return "int16";
    } else if constexpr (std::is_same_v<ValueType, uint16_t>) {
      return "uint16";
    } else if constexpr (std::is_same_v<ValueType, int32_t>) {
      return "int32";
    } else if constexpr (std::is_same_v<ValueType, uint32_t>) {
      return "uint32";
    } else if constexpr (std::is_same_v<ValueType, int64_t>) {
      return "int64";
    } else if constexpr (std::is_same_v<ValueType, uint64_t>) {
      return "uint64";
    } else if constexpr (std::is_same_v<ValueType, size_t>) {
      return "size";
    } else if constexpr (std::is_same_v<ValueType, double>) {
      return "double";
    } else {
      static_assert("unsupported ValueType");
    }
  }
  ....
};

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

Попробуйте найти сами
Попробуйте найти сами

Ответ:

Анализатор PVS-Studio выдаёт предупреждение V591 Non-void function should return a value. parameters.h 222

На первый взгляд предупреждение странное и смахивает на ложное срабатывание, ведь не может быть, что функция закончила работу, не вернув значение с помощью оператора return. Если выбирается ветка else, то там static_assert, и код просто не должен скомпилироваться. Во всех остальных случаях есть return "что-то";.

Но есть нюанс!

Ещё раз посмотрите на эту строчку:

static_assert("unsupported ValueType");

static_assert используется неправильно: пропущен bool-constexpr. Вернее, строковый литерал неявно конвертируется в значение true, и static_assert никогда не прервёт компиляцию. В итоге else-ветка функции ничего не возвращает, и её поведение будет не определено для всех специализаций NumericParameter, кроме указанных ранее в цепочке if constexpr.

Правильный вариант:

static_assert(false, "unsupported ValueType");
Теги:
+25
Комментарии9

Баги на всех языках мира. Проверка LanguageTool

Всем привет! Hello, everyone! Hallo zusammen! Hola a tothom! مرحباً بالجميع!

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

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

Теги:
Всего голосов 3: ↑3 и ↓0+4
Комментарии0

Слои валидации

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

Валидация на клиенте (если он есть)

Сюда входит формат данных, обязательность полей и так далее. Чуть сложнее, когда надо проверять, например, уникальность имени пользователя или емейла, в этом случае придется ждать отправки или делать запросы на бекенд во время заполнения. Главное что надо знать про эту валидацию, то что она вспомогательная. Клиент всегда можно обойти и сделать запрос напрямую. Дублирование как ни крути, хотя и важно для UX.

Структурная валидация

Это валидация, которая, обычно, происходит на уровне самого фреймворка или в самом начале цикла обработки запроса. Для этого данные прогоняются через валидаторы json схемы, которая в идеале генерируется из openapi спеки. В самых деревянных случаях ручками (так делать не надо). Что важно понимать, это не доменная валидация (бизнес правила). Да тут можно проверить формат, наличие/отсутствие, но нельзя и не правильно пытаться проверять уникальность, выполнение каких-то условий, например количества денег на счету и тому подобное.

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

Доменная валидация

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

Доменные проверки, как и клиентские могут дублироваться, если завязаны на консистентность базы данных. Например во многих фреймворках (с orm) есть валидация на уникальность, которая делает sql-запрос, но в документации у этого валидатора всегда написано, что это не надежно (из-за конкурентности) и в таких ситуациях обязательно делать индексы в базе данных.

В случае провала такой валидации, в api принято возвращать код 422

Валидация на уровне базы данных

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

Проверка корректности данных

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

Подобные проверки делают в первую очередь на клиенте (и на этом можно было бы остановиться). Внутри бека их располагают в слое форм (есть далеко не во всех фреймворках), либо некоторые фреймворки типа rails для простоты пихают такой валидатор в модели, хотя семантически это неверно.

Больше про разработку в моем телеграм-канале Организованное программирование

Теги:
Всего голосов 5: ↑4 и ↓1+3
Комментарии0

Участие в нескольких проектах снижает результаты работы — так ли это?

На одной из конференций прозвучал тезис: заказчику необходимо держать аутсорс-команду full-time. Потому что, работая на нескольких проектах одновременно, разработчики меньше погружаются в контекст каждой задачи, что в итоге сказывается на качестве кода и проекта в целом.

Была высказана и противоположная точка зрения, но хочется услышать мнение именно лидов и разработчиков, основанное на опыте.

Почему спрашиваю?

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

Вопрос к сообществу:

Правда ли, что разработчик, участвующий в нескольких проектах part-time, будет менее эффективен, допустит больше багов и в целом ухудшит качество релизов? Или это миф, и всё зависит от процессов, коммуникации и личной организованности?

Теги:
Всего голосов 3: ↑3 и ↓0+5
Комментарии17

Этот финт сэкономит вам время и нервы

Хочу написать о финте, который позволит вам сохранить нервы и сэкономить время. Правда некоторые (многие, почти все) впадают в ступор от него. Поэтому тут использована КДПВ с поста. Я наверно чувак слева.

А именно добавление первым условием if единицы:

if (1
    && $cond1
    && $cond2
    && $cond3
)

Использование финта дает нам возможность:
1. Быстро выключать фичу заменой 1 на 0:

if (0
    && $cond1
    && $cond2
    && $cond3
)

2. Быстро выключать любое условие в PhpStorm через горячие клавиши:

if (1
//    && $cond1
    && $cond2
    && $cond3
)

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

if (
    /*$cond1
    &&*/ $cond2
    && $cond3
)

Или такую:

if (
    $cond2
    && $cond3
)

3. Быстро добавлять новое первое условие:

if (1
    && $cond2
    && $cond3
)

легко превращается в:

if (1
    && $cond1 // в изменениях одна строка
    && $cond2
    && $cond3
)

4. Быстро дублировать любое условие.

5. Быстро менять порядок условий.

6. Также у нас будет чистый diff git-а при удалении/добавление первого условия.
Тут должен быть рисунок удаления с финтом и без, рисунок добавления с финтом и без.
Также при конфликте у нас будет более простое его решение, если нужно просто добавить оба условия.

Данный финт сродни правилу хорошего тона добавлять после последнего элемента массива запятую.
Это дает нам возможность при добавлении работать только с одной строкой. Добавлять горячими клавишами дублирования строк, в diff опять же будет только 1 строка, а также при конфликте слияний просто применяем обе строки и не нужно проставлять запятые, а то код упадет.

Теги:
Всего голосов 21: ↑15 и ↓6+11
Комментарии14

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

1. Давать осмысленные имена сразу же

Хорошие названия переменных, функций и классов экономят время всей команде: код проще читать, легче понимать и поддерживать. А еще чем меньше вопросов «что делает эта функция?» или «что содержит переменная?», тем лучше.

2. Декомпозировать код и избегать вложенности

if внутри if или for внутри for путают: каждое разветвление создает еще одну ветку, которую приходится держать в голове. Лучше разбить логику на небольшие части — код становится прозрачнее и надежнее.

как не надо:

функция заказать_пиццу(адрес):
  если адрес_валиден(адрес):
    если у_ресторана_ингредиенты():
      если клиент_может_платить():
        печать "Пицца заказана!"
      иначе:
        печать "Недостаточно денег"
    иначе:
      печать "Нет ингредиентов"
  иначе:
    печать "Адрес некорректный"

как надо:

функция заказать_пиццу(адрес):
  если не адрес_валиден(адрес):
    печать "Адрес некорректный"
    вернуть
  
  если не у_ресторана_ингредиенты():
    печать "Нет ингредиентов"
    вернуть
  
  если не клиент_может_платить():
    печать "Недостаточно денег"
    вернуть
  
  печать "Пицца заказана!"

3. Регулярно делать рефакторинг

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

4. Настроить линтер и форматер

Линтер — статический анализатор кода, который следит за определенным стилем написания кода. Так как у каждого из нас свой подход, нам нужен «инструмент-судья», который беспристрастно оценит оформление кода. Форматер помогает автоматически исправить код и привести его к единому виду. 

5. Комментировать только неочевидную бизнес-логику

Комментарии полезны, если они объясняют то, что нельзя понять из кода. Например, когда понимаем, что участок кода содержит особенность бизнес-логики, которая еще не ясна новому сотруднику. Но важно помнить, что избыток пояснений превращает понятный код в мешанину из кода и комментариев. Принцип простой: объясняем редкие, действительно сложные места и не трогаем остальное.

Теги:
Всего голосов 1: ↑1 и ↓0+1
Комментарии3

Почему нужно использовать DTO

Data Transfer Object, термин, который для разработчиков на статических языках является чем-то самим разумеющимся, но вот остальные его могут не знать (даже если пользуются). Хотя в эпоху интеграций, фронтенд-бекенд, сервис-сервис, очереди, это крайне важная конструкция.

DTO это очень промежуточный объект между моделью в вашем коде и данными, которые вы отдаете наружу или принимаете от внешней системы.

  • Модель => DTO => json/protobuf/sql...

  • json/protobuf/sql... => DTO => Модель

Нафига? Почему не сразу преобразовывать из, допустим, json в нашу модель или наоборот? Тем более во всех экосистемах есть механизмы, которые позволяют упаковывать любые объекты, задавая правила преобразования через метаданные, аннотации или еще как-то. Пример из Java:

@Entity
public class User {
    @Id
    private Long id;
    @JsonIgnore              // приходится скрывать
    private String passwordHash;
    @JsonProperty("created_at")
    private LocalDateTime createdAt;

    // getters/setters ...
}

var json = new ObjectMapper().writeValueAsString(dto);

Существует масса причин, почему это плохая идея. Для начала, это банальное нарушение MVC архитектуры. Модель начинает знать как о представлении, о том какие поля надо выдавать наружу, какие нет, как их переименовывать и так далее. Если это кажется натянутым, то вот вам реальные последствия.

Одна и та же сущность для внешнего мира редко представляется одним способом. В зависимости от задачи, это может быть один набор полей или другой. Как это разрулить? Дальше, здесь плохо контролируется процесс, легко может быть такое, что новое поле автоматически попало наружу, хотя вы этого не планировали, но забыли его исключить. А если нужны вычисляемые поля или другое представление (всегда в датах)? В такой ситуации модель будет наполняться доп свойствами и методами, которые готовят доп данные для преобразования, что ведет к сильному загрязнению кода. Что из этого относится к бизнес-части, а что к представлению? Проблема.

DTO позволяют отделить представление от модели в коде, создавая по сути промежуточный слой. Имея его, вы можете независимо развивать свою модель и API для взаимодействия с ним. И да, это один из аспектов MVC, конкретно Model-View.

Готовые DTO гораздо легче чем модели конвертировать в типы на TS если у вас есть такая потребность. Например мы наши DTO (используем Alba), превращаем в типы TS с помощью готового инструмента (Typelizer). С моделями так легко не получится.

За это конечно придется заплатить. В проекте появится папка, с большим количеством файлов. Но это с лихвой компенсирует все описанные выше проблемы. DTO очень простые и для их создания далеко не всегда надо с нуля писать классы. В той же java они генерируются с помощью mapstruct, в других языках свои механизмы.

Но это только базовая история. Если мы еще подключаем инструменты генерации из sql (как в go) или openapi как везде, то те самые DTO создаются вообще автоматически на основе описаний.

INSERT INTO links (original_url, short_name)
VALUES (sqlc.arg(original_url), sqlc.arg(short_name))
RETURNING *;

DTO:

type CreateLinkParams struct {
	OriginalUrl string `json:"original_url"`
	ShortName   string `json:"short_name"`
}

Причем для update будет создана своя структура:

type UpdateLinkParams struct {
	OriginalUrl string `json:"original_url"`
	ShortName   string `json:"short_name"`
	ID          int64  `json:"id"`
}

Здесь отличается только id, но в реальных кейсах, отличий в создании или обновлении одной сущности обычно значительно больше, поэтому количество DTO тут становится еще больше.

DTO, кстати, должны быть имутабельны, иначе туда потечет логика

Больше про разработку в моем телеграм-канале Организованное программирование

Теги:
Всего голосов 9: ↑7 и ↓2+6
Комментарии1

Как анализировать C и C++ код без привязки к сборочной системе на Windows

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

Иногда бывает так, что появляется целый "зоопарк" самописных скриптов сборки, а его последний "смотритель" уволился ещё в прошлом году (играет Гражданская Оборона — "Зоопарк").

Хотелось бы всё равно как-то анализировать такой код без необходимости разбираться в хрупкой и непонятной системе сборки. Что же делать?

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

В новой статье посмотрим, как воспользоваться этим механизмом для ОС Windows в анализаторе PVS-Studio, и  как сделать его использование в процессе разработки удобным.

Теги:
Всего голосов 5: ↑4 и ↓1+4
Комментарии0

Статья "Код блокчейн-проектов Neo и NBitcoin VS анализатор кода. Кто-кого?"

PVS-Studio ворвался в мир блокчейн-разработки, и первыми "под удар" попали open source проекты на C# — Neo и NBitcoin!

В статье мы рассмотрели самые интересные ошибки: как явные, так и потенциальные, которые нашли в этом проекте. Если вам интересно, какие ошибки могут находить такие инструменты, как PVS-Studio, или вы желаете прокачать свой собственный "ментальный анализатор", приглашаю к прочтению :)

Теги:
Всего голосов 5: ↑5 и ↓0+6
Комментарии0

А всё таки, когда моки зло, а когда нет?

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

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

Последний случай, описывает процесс мокирования. То есть мок, это когда мы проверяем то, как код что-то делает, а не что он делает. Иногда говорят, что мы тестируем методом white-box, потому что мы знаем как конкретно написан тест и завязываемся на это, а не на результат работы этого кода, как в black-box тестировании.

Когда мы проверяем как код работает, мы связываем тест с внутренней реализацией. Любое изменение внутри функции (например, вызов другого метода или смена порядка действий) может поломать тест, даже если внешнее поведение программы остаётся тем же. В итоге тест перестает быть защитой от ошибок и превращается в тормоз для рефакторинга. В подкасте про спринг я услышал классный термин: "бетонирование кода", вот это оно и есть.

Когда же моки все таки нужны? Допустим мы пишем систему с поддержкой хуков, например фреймворк для тестирования. В тестах такого фреймворка вполне допустимо проверить что хуки setup, teardown, beforeSetup, afterSetup и так далее, вызываются в нужном порядке и с нужными аргументами.

Общее правило здесь такое, если код можно тестировать методом black-box, то лучше так и делать. White-box это вынужденная необходимость, когда по другому ни как. Правда будьте осторожны, нередко программисты только думают, что по другому никак, потому что они не знают других подходов или по каким-то причинам решили, что другие подходы не подходят по идеологическим причинам.

Гораздо чаще же в тестах нужны не моки, а стабы, которые позволяют изолировать код от внешних эффектов.

Например:

  • База данных, которая хранит данные в памяти.

  • Фейковые сервисы какого-нибудь облака, например AWS

  • Поддельный HTTP клиент, который возвращает заранее заготовленные ответы.  

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

Все эти решения делают тесты быстрыми, предсказуемыми и независимыми от инфраструктуры, при этом вы все еще проверяете поведение системы снаружи, не нарушая принцип black-box.

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

Итого

Моки используются тогда, когда вы хотите проверить как написан ваш код. Стабы используются тогда, когда вы хотите, чтобы внешние эффекты не мешали вам тестировать результат работы вашего кода.

Больше про разработку в моем телеграм-канале Организованное программирование

Теги:
Всего голосов 4: ↑2 и ↓20
Комментарии0

Модель искусственного интеллекта с открытым исходным кодом для оценки риска рака молочной железы

FRA-RIG-breast, экспериментально исследовательская модель, построенная на основе Фрактально Референциальной Архитектуры (FRA) — фреймворка, который интерпретирует данные с помощью моделей различий, а не изолированных признаков.

Модель делит входные параметры на три концептуальных блока:

R — особенности морфологической структуры и размера,

I — текстура, симметрия и фрактальные свойства тканей,

G — геометрическая компактность, кривизна и агрессивность.

Каждый блок генерирует свой собственный внутренний индекс (S_R, S_I, S_G), а конечная вероятность вычисляется с помощью логистической регрессии.

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

Ключевые результаты

Средние показатели перекрестной проверки:

Точность — 0,967

ROC-AUC — 0,989

PR-AUC — 0,990

F1-оценка — 0,973

Порог классификации (Youden) = 0,64

Модель сохраняет точность на 96-98% при сгибании и демонстрирует высокую стабильность между запусками.

Цель состоит в том, чтобы продемонстрировать прозрачный, поддающийся интерпретации искусственный интеллект для онкологических исследований — без сложного глубокого обучения.

FRA-RIG-breast может быть распространен на другие области, такие как исследования крови, легких или кожи, где понимание влияния факторов так же важно, как и само прогнозирование риска.

Zenodo: https://zenodo.org/records/17492410

GitHub: https://github.com/AdmailFRA/FRA-RIG-breast

Лицензия: MIT бесплатная для использования в научных исследованиях и образовательных целях.

❗️ Это экспериментальная модель, а не медицинский диагностический инструмент.

Если вы работаете в области онкологии, биоинформатики или обработки данных и хотите узнать, как подход FRA может быть адаптирован к вашему подтипу рака, не стесняйтесь обращаться .

Буду рад обсудить, как можно было бы точно настроить параметры R, I и G для вашего исследовательского контекста. Безвозмездно.

Теги:
Всего голосов 2: ↑0 и ↓2-2
Комментарии1

Почему "давайте писать внимательнее" - плохое решение?

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

Инженерное решение должно быть проверяемым:

  • тест падает - гипотеза неверна;

  • алерт сработал - защита работает;

  • фича-флаг не дал багу уйти - система выдержала.

"Внимательность" не измеряется, не тестируется и не гарантируется. Это не решение, а успокоение.

Теги:
Всего голосов 3: ↑2 и ↓1+2
Комментарии6

Ближайшие события

Управление сложностью

Со временем, сложность проектов только растет. Какие бы мы изменения в коде не делали, переходили на новые фреймворки, базы, языки или подходы, алгоритмическая сложность (то что в бизнес логике) будет становиться только выше. Технические улучшения максимум могут убрать случайную сложность, когда мы выбрали неверный или не самый эффективный инструмент, но если с точки зрения логики нужно выполнить 30 разных сценариев, мы их запрограммируем в любом случае независимо от выбранных технологий.

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

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

Грамотное управление состоянием

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

Изолированная сложность

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

Приоритеты слоев

Ошибки на уровне формирования моделей и их связей, решают намного больше чем ошибки допущенные при выводе этих данных в api или на фронтенде. Вывод это всегда терминальная стадия, его результаты никак не используются в коде, а вот модели и то как организованы связи, это основа всего, что пронизывает все приложение на самом глубоком уровне. Если тут накосячить, страдать будем в каждой точке сталкивания. Можно сказать что порядок приоритета такой:

модели + структура базы => обработчики (контроллеры, сервисная история) => вывод (сюда же переводы и работа со строками)

Публичные контракты (API)

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

Отложенные решения

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

- Все, что можно поменять без боли - оставляем простым.- Все, что будет трудно поменять (API, модели, схемы БД, протоколы взаимодействия) - продумываем особенно тщательно.

Больше про разработку в моем телеграм-канале Организованное программирование

Теги:
Всего голосов 6: ↑4 и ↓2+2
Комментарии2

Какая должна быть длина у функций?

Щас скажу кое-что неочевидное. На эту проблему нельзя смотреть в статитке. Вот правильно 4 строки, 10, 100, поэтому разбиваем как-только доходим до предела. Я смотрю на это в динамике.

Когда мы только что-то пишем и это не очевидная абстракция вроде проверки числа на простоту, то разбивать на функции не надо, до тех пор пока вы не начнете упираться во что-то начиная от необходимости повторного использования (а значит выделения доп абстракций) до большого количества состояний, которые делают анализ функции слишком сложным. Какой при этом получится размер? Да хрен его знает, в реальной жизни функции бывают очень разные, это легко проверить если походить по опенсорсу на гитхабе. И мы говорим про очень успешные продукты и проекты.

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

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

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

p.s. Смог тут загуглить исследование сотен миллионов строк на гитхабе: https://arxiv.org/pdf/1806.04556 (на скрине выдержка)

Больше про разработку в моем телеграм-канале Организованное программирование

Теги:
Всего голосов 5: ↑4 и ↓1+4
Комментарии3

🚀 Как я решаю сложные задачи

Для меня самое важное — полностью понять задачу ещё до того, как начать её выполнять.

Звучит банально, но под полным пониманием я имею в виду прям ПОЛНОЕ: не только суть, но и все технические детали, вплоть до того, какой код и где нужно написать.

📝 Часто я оставляю туду в коде и фиксирую шаги, которые нужно сделать. После этого перехожу к следующему этапу и разбираю его отдельно.

💡 Почему это работает:
1️⃣ Это помогает точнее оценить время выполнения.
2️⃣ Легче расставлять приоритеты. Если застрял на мелочи — видишь общую картину и не утонешь в деталях.
3️⃣ Быстрее формулируются вопросы. Особенно важно, когда коллеги в другой таймзоне и доступны не всегда. Чем чётче и быстрее задаёшь вопросы, тем быстрее получаешь ответы.

⚡️ Поэтому, когда я понимаю, что задача сложная, я не спешу решать её сразу. Вместо этого я стараюсь как можно скорее задать правильный вопрос.

👨‍💻 Джуниор

Теги:
Всего голосов 6: ↑3 и ↓3+1
Комментарии3

Как научиться программировать лучше

Часто встречаю такое мнение, что главное то как мы систему проектируем сверху и не очень принципиально, что там внутри. То есть вот у нас есть модули, ответственности и дальше как-то оно реализуется.

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

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

Для меня это всегда выглядело как техника в спорте. Сначала мы, упорно и монтонно ставим технику, а уже затем двигаемся дальше. Без техники спортсмен всегда будет проигрывать другим. В программировании мы конечно не соревнуемся друг с другом, но результат будет примерно такой же, на выходе получится такая себе архитектура.

И даже если вы со мной согласитесь, дальше начинается вакханалия. А что является базой, которую надо уметь и знать? Готов поспорить, что больше всего будут кричать про SOLID, чистый код и другое похожее мракобесие. Да, в любых шутках есть какие-то полезные мысли, но это все настолько скрыто за ширмой конкретных кейсов делай раз и делай два, что картинка теряется и все скатывается в срач, одна ответственность у функции или две.

Мой личный топ того, что учит писать грамотный код на высокоуровневых языках, где мы фокусируемся на создании правильных абстракций это SICP/HTDP + попрактиковаться в написании кода на одном из популярных функциональных языков.

За мою довольно длительную карьеру, самый большой сдвиг произошел именно в тот момент, когда от изучения фаулеров, мартинов и банд я взялся за более серьезный фундамент. Было это году в 2013, с тех пор, что бы я не изучал, оно накладывается и дополняет, а не переворачивает мой мир внутри, как было 5 лет до того. Тогда, в первые годы моего программированияб каждый раз смотришь на свой код или читаешь что-то и думаешь, блин, надо проектировать по другому.

И это не только мое наблюдение, почти все ребята с кем мы тогда активно тусили в разных комьюнити, пересекались на конфах и дружили, в целом отмечали как фп (в частности clojure, haskell, ocaml, erlang) значительно сдвигали понимание программирования.

Почему это так? А потому что в остатке мы упираемся в побочные эффекты, барьеры абстракции (тут сикп) и грамотное управление состоянием. Вот такие пироги

Больше про разработку в моем телеграм-канале Организованное программирование

Теги:
Всего голосов 10: ↑8 и ↓2+7
Комментарии2

Выводим Бугаенко на чистую воду разбирая ООП

Топ Перлов

  • Любой массив байт должен уметь работать с файлами, сетью и тд.

  • Программа должна не падать на ошибках, а продолжать работу с фейковыми объектами.

  • Вместо падения в моменте конструирования объекта, надо падать на другом конце программы при каждом его использовании.

  • Я придумал новый язык, и чтобы он не так сильно тормозил, надо встроить GC в CPU.

Упомянутые ссылки

Копилка благодарностей

Теги:
Всего голосов 13: ↑5 и ↓8-3
Комментарии2

Unit-тесты во фронтенде: развеиваем мифы

После статьи о навыках джуниоров многие не согласились с моей оценкой unit-тестов. Давайте посмотрим, где они действительно полезны, а где создают иллюзию ценности.

Если вы начинающий разработчик, вас наверняка убеждали:
«Без unit-тестов никуда! Всё должно быть покрыто тестами!»
Но так ли это на самом деле?

Где unit-тесты полезны:

  • Бизнес-логика и утилиты (форматирование данных, расчёты)

  • Кастомные хуки (управление состоянием, формы)

  • Критичные функции (редкий зверь во фронтенде)

Где они бесполезны (и даже вредны):

  • UI-компоненты (скриншотные тесты часто ломаются из-за изменений вёрстки)

  • API с моками (моки не показывают реальное поведение сервера)

  • Тестирование библиотек (проверяете чужой код)

Что использовать вместо?

  1. Интеграционные тесты — проверяют реальные сценарии

  2. Zod для валидации API — предотвращает ошибки из-за неожиданных данных

  3. Ручные проверки — быстрее и точнее, чем скриншотные тесты

Для джуниора unit-тесты — не приоритет. Важнее:

  • Глубокое изучение фреймворка

  • Умение работать с API

  • Навык чтения и отладки кода

Не стоит тратить время на «тесты ради тестов». Сосредоточьтесь на том, что действительно поможет в работе.

Теги:
Рейтинг0
Комментарии0

Красные флаги в код-ревью: перегибы, которых лучше избегать

  1. Фокус на мелочах. Если уделять много внимания опечаткам и код-стайлу, то времени уйдет много, а реальных результатов будет мало. Главное — это всё-таки архитектура, алгоритмы и производительность, а косметика только на втором плане. Зачем портить настроение себе и команде, если можно ничего никому не портить?

  2. Поверхностный анализ. Забивать на проверку кода — тоже плохая идея. И дело даже не в багах, их отловит тестировщик. Дело в безопасности всей системы. Если какой-нибудь хакер найдет уязвимость, которую QA не нашел, — жди беды. Ну и техдолг никто не отменял: если не делать сразу хорошо, однажды придется переделывать.

  3. Токсичность. Во время код-ревью лучше всё-таки искать потенциальные проблемы с кодом, а не учить коллег работать. То есть фокус внимания — не на поиске недостатков в чужой работе, а на самом коде. А личные предпочтения и вкусы относительно кода лучше обсуждать отдельно.

Обратную связь стоит давать вдумчиво — подбирать слова и контролировать интонацию. Фидбек должен быть таким, чтобы у разработчика не возникало ощущения, будто он ни на что не годится.

А каким должен быть хорошее код-ревью — в нашем блоге.

Теги:
Всего голосов 8: ↑4 и ↓40
Комментарии0

Вклад авторов