Вы когда-нибудь боялись нажать на кнопку «релиз» CI? Слова «баги» и «регрессии» вам знакомы?
Пришло время оставить страх позади и начать создавать приложения более высокого качества!
В этой статье я постараюсь показать вам, почему качество кода важно и как улучшить его в вашем приложении Flutter с помощью различных инструментов и техник. Надеюсь, к концу этой статьи у вас будут ключи к более уверенному созданию приложений 🚀.
- Вы сказали качество кода? 🤔
- Повышение уверенности и репутации 👍.
- Увеличение повторного использования кода ⌨️
- Какие метрики определяют качество кода приложения? 📊
- Качество кода во Flutter 📱
- Линтеры ℹ️
- Рефакторинг и особенности IDE 🧑💻
- Тесты ✅ ❌
- Покрытие кода
- CI & CD (Continuous Integration & Continuous Deployment)
- Измерение качества кода
- Заключение
Вы сказали качество кода? 🤔
Прежде чем перейти непосредственно к решениям, я думаю, важно сделать небольшое напоминание о том, что такое качество кода, и почему оно (очень очень очень очень) важно.
Качество кода — это метрика, которая обычно говорит о том, хорош или плох данный код. Очевидно, что это очень субъективно и отличается от одного проекта к другому, но это не имеет значения, потому что цели, стоящие за качеством кода, почти всегда одинаковы:
Повышение уверенности и репутации 👍.
Уверенная поставка программного обеспечения чрезвычайно важна, потому что таким образом вы не боитесь новых релизов, и поскольку вы поставляете с большей уверенностью, вы можете поставлять чаще, что делает ваш цикл поставки добродетельным кругом. И как разработчики мы всегда рады, что нам не приходится работать в выходные, потому что последний релиз был полон ошибок. 🦟 🪲
Меньше риска, больше удовольствия.
Очевидно, что на карту поставлена и репутация вашей компании: чем выше качество программного обеспечения (быстрота, безопасность, надежность и т.д.), тем больше уверенности у ваших пользователей, тем больше они будут доверять вам и продолжать использовать ваше приложение (и даже рекомендовать его другим).
Увеличение повторного использования кода ⌨️
Если только вы не разрабатываете одноразовый POC (и даже тогда, остерегайтесь эффекта «POC to production»!), вы потратите много времени на разработку и поддержку вашего приложения: это могут быть недели, месяцы или даже годы! Чем более качественный код вы заложите вначале, тем меньше затрат у вас будет на дальнейшие разработки в будущем.
С другой стороны, низкокачественный код не только заставит вас платить больше в долгосрочной перспективе, но и может нанести реальный ущерб бизнесу вашей компании, поскольку любое изменение в приложении может стать очень сложным и потенциально заморозить ваш проект.
Какие метрики определяют качество кода приложения? 📊
Я не буду вдаваться в подробности здесь, потому что другие статьи уже описывают это, но в основном мы можем найти :
- Надежность. Насколько вероятно, что наше приложение будет работать без неожиданных сбоев.
- Ремонтопригодность, расширяемость и возможность повторного использования. Насколько легко добавить новый код к существующему или изменить существующий.
- Тестируемость. Насколько легко тестировать приложение.
- Переносимость. Насколько хорошо ваше приложение будет вести себя в различных средах. Для мобильного приложения Flutter вы должны убедиться, что ваше приложение одинаково хорошо работает как на Android, так и на iOS. Для веб-приложения или приложения для настольных компьютеров вы также можете захотеть протестировать несколько браузеров или платформ ОС.
Качество кода во Flutter 📱
Качество кода во Flutter ничем не отличается от качества кода на любом другом языке, фреймворке или вообще в любом другом месте. Линтеры, тесты, CI, обзоры, рефакторинг, конвенции кодирования, инструменты измерения качества кода…
К счастью для нас, во Flutter есть все это!
Сейчас я покажу несколько инструментов, которые можно использовать для улучшения, гарантии и измерения качества кода во Flutter. Обратите внимание, что мой список может быть неполным (пожалуйста, добавьте свои инструменты в комментариях!), и он не имеет определенного порядка. Наконец, важно понимать несколько вещей, говоря о качестве кода:
- Хорошие/экспериментирующие разработчики, как правило, пишут более качественный код. Не полагайтесь только на инструменты, приведенные ниже, чтобы утверждать, что ваш код хорош, лучше подумайте о коде, который вы пишете, и постарайтесь написать лучший код, на который вы способны. Инструменты здесь только для того, чтобы помочь вам, но они не будут думать за вас. Это особенно верно для деловых вопросов, где инструменты вряд ли могут проверить, что на самом деле ожидается.
- Не стоит выбирать один инструмент и думать, что его будет достаточно для хорошего качества кода. Многие инструменты должны использоваться вместе для постепенного улучшения качества кода и его измерения. И в конце концов, не забывайте, что инструменты могут делать только то, что вы хотите, и их полезность зависит от того, что вы с ними делаете. Например, хорошо иметь тесты, но если никто в команде никогда не смотрит на их успешность или неуспешность, они практически бесполезны.
Линтеры ℹ️
Линтеры — это простые программы анализа кода, которые способны обнаружить распространенные ошибки программирования и синтаксические проблемы. Принцип работы линтеров заключается в определении набора правил, которые обычно можно настроить, и эти правила будут проверяться командной строкой или IDE каждый раз, когда добавляется/обновляется/удаляется новый код.
Хорошая новость: начиная с версии Flutter 2.10, пакет flutter_lints добавляется по умолчанию в ваш pubspec.yaml
, когда вы создаете новое приложение с помощью командной строки Flutter flutter create
:
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.1
Вместе с этим вы также получите файл analysis_options.yaml
в корне вашего проекта, который будет включать исходный пакет :
include: package:flutter_lints/flutter.yaml
linter:
rules:
Итак, что мы имеем здесь? Довольно просто, мы просто говорим, что хотим использовать предварительно настроенный набор правил из пакета flutter_lints. Радость от открытого исходного кода, вы можете перейти к фактическому репозиторию, чтобы проверить, какие правила определены.
Теперь вы должны помнить, что этот набор правил был определен командой Flutter как минимальный набор рекомендуемых правил. Хотя мы можем доверять команде, разработавшей фреймворк, в том, что она даст нам хорошие правила, вы всегда можете настроить его и деактивировать или активировать отдельные правила.
Скажем, если я хочу ввести обязательное объявление типов возврата и запятых в конце, мне просто нужно обновить файл analysis_options.yaml
:
include: package:flutter_lints/flutter.yaml
linter:
rules:
always_declare_return_types: true
required_trailing_commas: true
И если вам интересно, какие правила существуют, полный список здесь! На мой взгляд, всегда полезно взглянуть на существующие правила, чтобы понять, как лучше писать код на Dart и Flutter, и понять, чего следует избегать.
В моем случае я обычно настраиваю множество правил, чтобы определить кодовые соглашения, которые работают для моей команды. Мне нравится иметь правила linter по умолчанию в новых приложениях, но по умолчанию linter устанавливает около 44% правил, что довольно слабо, я предпочитаю иметь более сильные кодовые соглашения, чтобы все в команде использовали практически одинаковый код и могли читать чужой код. Я обычно активирую около 80% правил.
Если вы хотите узнать больше о линтинге Flutter, включая сравнение различных популярных пакетов, вам обязательно нужно прочитать статью Майка Райдсторма о линтинге Flutter, и он даже создал таблицу Google Sheet для сравнения пакетов линтинга по правилам!
Наконец, чтобы вывести линтеры на новый уровень, взгляните на Dart Code Metrics. Этот пакет продвигает правила линтера немного дальше, проверяя дополнительные правила, более сложные антипаттерны или метрики кода, а также проверяя неиспользуемые файлы и строки l10n.
О, и пока я не забыл, если вы новичок в языке Dart или чувствуете, что вам нужно подкрепиться, вам обязательно нужно взглянуть на Effective Dart guidelines.
Рефакторинг и особенности IDE 🧑💻
Обычно я быстро определяю младшего разработчика на первой сессии парного программирования или парного обзора по тому, как разработчик использует свои инструменты. Я не хочу никого обидеть, я просто говорю, что современные IDE имеют много инструментов и функций, которые позволяют быстро и сильно рефакторить, поэтому мы должны использовать их и злоупотреблять ими, для этого они и существуют.
VSCode и AndroidStudio (вероятно, используемые 99,99% разработчиков Flutter в настоящее время) включают мощные инструменты для рефакторинга. Хотите ли вы переименовать поле или метод, извлечь блок кода в метод или встроить метод в другой, вы, вероятно, можете делегировать эту громоздкую задачу своей IDE:
И если вы делаете это часто, выучите ярлык и станете более эффективным в своей повседневной жизни!
Но в вашей любимой IDE есть много других (не очень) скрытых жемчужин.
Хорошим примером является quickfix, который будет применять исправления к распространенным ошибкам / предупреждениям:
И знаете что? Замечательно то, что ваша IDE способна прочитать набор определенных правил в вашем файле analyis_options
и затем покажет вам ошибки / предупреждения / подсказки прямо в соответствующих файлах. Насколько это здорово?
Еще одна функция, которая спасла мою жизнь разработчика Flutter, когда я только начинал, это функция автодополнения. Это просто безумие, как быстро вы можете создавать виджеты, оборачивать виджеты в другие и делать множество классных вещей. Если вы сейчас добавляете виджеты и считаете, сколько у вас закрывающих скобок, просто остановитесь и воспользуйтесь автозавершением:
Вы поняли, IDE мощные и любят работать на вас, так что помогите себе и используйте их. Я оставлю вас на части IDE с функцией автоформатирования, особенно используемой во Flutter, если вы не хотите сойти с ума:
Тесты ✅ ❌
Да, тестирование необходимо для поддержания высокого качества кода. Оно полезно на этапе проектирования, потому что для того, чтобы компонент был тестируемым, необходимо правильно его спроектировать (изолированно, не должен делать слишком много и т.д.). Это также очень полезно, потому что, если все сделано правильно, это повторяющийся шаг, который можно автоматизировать для проверки отсутствия регрессий по мере добавления функций и исправления ошибок.
Я не буду слишком подробно останавливаться на тестировании, поскольку официальная документация уже содержит исчерпывающую главу о тестировании.
В документации различают три типа тестов:
- Юнит-тесты проверяют одну функцию, метод или класс.
- Виджет-тесты (в других UI-фреймворках называемые компонентными тестами) тестируют один виджет.
- Интеграционные тесты тестируют полное приложение или большую часть приложения.
Если вы не знаете разницы между этими тестами, пожалуйста, прочтите документацию, это очень важно знать, какой из них что делает.
Однако я считаю, что есть четвертый тип тестов, который часто неизвестен разработчикам, но, на мой взгляд, очень мощный:
🏆 золотые тесты 🏆
Золотой тест — это просто особый вид теста виджетов, за исключением того, что мы используем тестовый фреймворк Flutter для проверки соответствия рендеринга нашего виджета тому, что мы предполагаем. Рендеринг виджета должен соответствовать предварительно сгенерированному изображению, называемому «золотым файлом», отсюда и название теста.
Я не буду вдаваться в подробности, поскольку в других статьях это уже сделано, но в основном вам нужно помнить, что золотые тесты — это тесты виджетов, что означает, что они выполняются почти так же быстро, как тесты виджетов, и гораздо (гораздо, гораздо) быстрее, чем интеграционные тесты, и позволяют быстро протестировать визуальный рендеринг наших виджетов.
Вот пример, где я сгенерировал золотой файл для виджета плитки, как на светлой, так и на темной темах (с одним золотым тестом):
На случай, если что-то пойдет не так, генерируются изображения сбоев, которые бывают в нескольких вариантах, например, в режиме «maskedDiff», когда изображения накладываются друг на друга и только изменения выделяются цветом (розовым цветом ниже):
Или изолированное отличие, где показано только отличие:
Я просто хотел показать вам краткий обзор золотых тестов, я лично люблю их и использую для быстрой генерации вариантов изображений для различных параметров:
- Светлые и темные темы
- Различные ориентации (например, портрет / ландшафт)
- Различные размеры устройств и соотношение сторон пикселя (например, iPhone 5 и iPhone 13)
ℹ️ Только предупреждение, тестирование большого количества вариантов в каждом тесте быстро увеличит количество генерируемых изображений и общее время выполнения. Пример
4 devices sizes x 2 themes x 2 orientations = 16 tests (for a single test)
Вы — мастер, вы решаете, что вам нужно делать.
Последний совет по золотым тестам: посмотрите на пакет Alchemist, чтобы сделать вашу жизнь золотой ☀️. Альтернативой для золотых тестов является более известный пакет golden_toolkit.
И просто чтобы подвести итог, вот краткий обзор всех типов тестов во Flutter:
Покрытие кода
Прежде всего, оговорка: я не хочу начинать войну о том, следует ли использовать покрытие кода в качестве меры (или единственной меры) качества кода. Если вы полагаетесь исключительно на покрытие кода для измерения качества кода, то вам, вероятно, предстоит долгий путь… покрытие кода само по себе мало что значит, поскольку 100% покрытие куска кода не означает, что тесты хороши и действительно что-то проверяют.
Тем не менее, есть случаи, когда покрытие кода может быть интересно как часть глобального измерения качества кода, например, вы можете захотеть убедиться, что ваш код имеет достойное покрытие кода и не становится все ниже и ниже с течением времени (и добавлением кода).
Хорошая новость, Flutter позволяет вам сделать это с помощью простого аргумента командной строки --coverage
. Однако, чтобы действительно сгенерировать отчет о покрытии из необработанного результата, нам нужно использовать комбинацию либраири junit
и lcov
. Обычно в своих проектах я использую следующий скрипт:
#!/bin/bash
## Prepare coverage folder and JUnit report lib
mkdir -p coverage/
flutter pub global activate junitreport
## Run tests with coverage and JUnit report
flutter test --coverage coverage/ --machine | tojunit --output test_results.xml
## Generate coverage report
lcov --remove coverage/lcov.info
"lib/main.dart"
"lib/other_file_to_remove_from_coverage.dart"
-o coverage/lcov_cleaned.info
genhtml -o coverage/ coverage/lcov_cleaned.info
После выполнения этого скрипта вы должны получить отчет в папке coverage/
, который выглядит следующим образом:
Вот так!
CI & CD (Continuous Integration & Continuous Deployment)
Я хочу сказать только одно:
Вы должны использовать CI.
Вы должны использовать CI.
Вы должны использовать CI.
Хорошая платформа(ы) CI & CD необходима для успешного проекта по многим причинам, вот неполный список:
- Это экономия времени, потому что вам не нужно выполнять каждую команду вручную, как для сборки, так и для тестирования и развертывания ваших приложений.
- Это автоматизация, делающая сборки и развертывания повторяемыми.
- Это позволяет постоянно проверять множество переменных (успешная компиляция, успешные тесты и т.д.) и дает постоянную обратную связь с командой разработчиков: есть ли предупреждения о потенциальных регрессиях?
В любом случае, вы должны использовать CI & CD платформу.
Это не совсем специфическая для Flutter тема, но я должен был упомянуть ее. Единственная специфика сборки Flutter связана с iOS, потому что приложения для iOS могут быть созданы только на MacOS runner… да 🙌.
В остальном, вы можете использовать практически любую платформу или комбинацию платформ (и я использовал многие из них в своих различных клиентских проектах), если вы можете реализовать достойную сборку и развертывание (эти шаги могут быть разделены, потому что релизы обычно создаются вручную): GitLab CI, Github Actions, Bamboo, Bitrise, Azure Devops, Codemagic…
Кстати, я написал статью около 2 лет назад (возможно, сейчас она неактуальна…) о том, как развернуть приложения в магазинах с помощью Codemagic в сочетании с Fastlane.
В идеале вы хотите попытаться дать как можно больше обратной связи своим коллегам-разработчикам по каждой сборке: результаты проверок lint, неудачные тесты, включая золотые ошибки, результаты тестов, покрытие кода и т.д. Очень перспективный пакет Dart под названием Danger.dart делает именно это, и вот статья, которая объясняет этот процесс немного подробнее.
Измерение качества кода
Если вы используете инструменты, представленные в этой статье, у вас уже должны быть довольно хорошие показатели качества кода вашего приложения. Линтеры, тесты, покрытие кода и т.д.
Тем не менее, некоторые люди все еще хотят иметь некую приборную панель, чтобы собрать все показатели, связанные с качеством кода, в одном месте.
Ничего не напоминает? Не стесняйтесь, я знаю, что вы думаете о Sonarqube!
И к счастью для нас, теперь есть пакет Flutter / Dart, который можно подключить к Sonarqube для анализа наших любимых приложений!
Эта библиотека под названием sonar-flutter генерирует известную приборную панель Sonarqube после учета всех мер, связанных с Dart & Flutter:
Заключение
В этой статье мы увидели, что качество кода очень важно для того, чтобы сделать приложение Flutter более безопасным, надежным и поддерживаемым. Мы также рассмотрели несколько инструментов, которые могут облегчить нам жизнь при попытке улучшить качество кода в наших приложениях.
Обратите внимание, что существует еще много техник, которые я не упомянул ранее, например, парное программирование, обзоры кода, документирование или TDD. Поскольку это общие техники, я решил сосредоточиться на конкретных инструментах Dart или Flutter, чтобы дать вам хороший обзор того, что вы можете сделать. Очевидно, что вы можете использовать любую известную технику в дополнение к конкретным инструментам, если чувствуете в этом необходимость:
Ваша команда — единственный хозяин качества вашего приложения!