Необходимые фронтенд автоматизации
Введение
За годы работы с фронтендом я перепробовал кучу инструментов автоматизации — часть оказалась временным хайпом, а часть настоящими находками, которые я использую и по сей день.
Сегодня расскажу про свои любимые автоматизации, которые считаю полезными (а некоторые — прямо необходимыми). Все их я испытывал на проде, и кое-что из этого отличается от типичного пайплайна.
Дисклеймер
Намеренно не трогаю LLM-автоматизации по нескольким причинам:
- Их уже столько, что хватит еще на 1–2 статьи
- У меня пока мало опыта с LLM-автоматизациями именно на проде
- Они постоянно появляются, меняются и эволюционируют
- Текущие автоматизации можно использовать внутри LLM-агентов — они хорошо дополняют друг друга
Автоматизации
Статья идет от более известных инструментов к менее раскрученным. Можно пользоваться оглавлением слева и сразу прыгать к интересующей области.
Линтинг и форматирование = ESLint + ESLint Stylistic
Самая очевидная автоматизация — конечно же ESLint.
А где Prettier?
Да, Prettier здесь нет. На мой взгляд, лучше использовать ESLint Stylistic.
Подробнее об этом можно почитать в Why I don't use Prettier, но мой главный аргумент такой: если что-то из стиля Prettier тебе не нравится, ты будешь с ним бороться и городить костыли. Часть поведения нельзя тонко настроить, а что-то вообще нельзя переопределить. ESLint дает куда больше гибкости там, где она нужна, и при этом не жертвует полезными фичами.
Если вы еще не пробовали такой подход — очень советую.
Также стоит внимательно наблюдать за Oxlint и Oxfmt. На момент написания статьи Oxlint еще не поддерживает многие правила ESLint (общие, vue), но на момент прочтения все может измениться.
Тестирование = Playwright + ночные смоки
В тестировании автоматизаций полно, но одну недооценивают особенно сильно: ночные E2E-смоки. Смоук-тесты — это небольшой набор E2E-кейсов, который покрывает самые используемые сценарии продукта. Если хотите, чтобы продукт был максимально стабильным — запускайте их каждую ночь. Для E2E сейчас лучше всего, на мой взгляд, подходит Playwright — сильная поддержка от Microsoft, читаемее Cypress и есть все основные интеграции.
Самый крутой и недооцененный плюс — ловить регрессии в типовых сценариях до следующего рабочего дня, часто раньше, чем кто-то успеет заметить. Если никто не заметил, можно считать, что этого и не было. Чистая история ночных прогонов еще и хороший аргумент, когда речь заходит о надежности или повышении.
Качество кода = Knip + Husky
Knip помогает найти мертвый код. Минус Knip в том, что при auto-imports нужна дополнительная настройка — такие файлы по умолчанию выглядят неиспользуемыми. Минимальный фикс — пометить их как entry points или игнорировать:
{
"entry": ["src/main.ts", "src/components/**/*.vue"],
"ignore": ["src/auto-imports.d.ts"]
}
Husky позволяет запускать команды на git-хуках — чаще всего на pre-commit. Типичные сценарии: запуск lint-staged, чтобы линтить только staged-файлы, или проверка формата commit-сообщений.
Но мое мнение: я бы не использовал Husky, или как минимум не блокировал бы им коммиты. Те же проверки можно реализовать в CI, и там есть контролируемый способ их обойти, когда это реально нужно.
Блокирующие pre-commit хуки в приватных командах почти никогда не стоят того:
- Эти правила легко отключить через
HUSKY=0(или--no-verify) без какого-либо следа или отчета. - Если обход все же перекрыт (кастомные git-обертки, жесткие политики хуков и т.д.) — удачи с хотфиксами, которые надо выкатить ASAP. Попробуйте объяснить менеджеру, что фикс не вышел из-за ошибки ESLint. Плюс становятся недоступны временные коммиты (лично я ими часто пользуюсь).
- Даже это не гарантирует отключения правил — пользователь может просто удалить их или сам Husky у себя на машине.
В итоге поздравляю: вы сильно ухудшили DX, получив наполовину «защищенный» пайплайн.
Считать локальные хуки Husky настоящей границей безопасности — примерно как хранить JWT-секрет в браузере. На собеседовании присутствие блокирующих Husky-хуков в команде я бы серьезно считал желто-красным флагом.
Пожалуйста, используйте Husky только если понимаете, что делаете.
Версии зависимостей = taze + Renovate
Управлять зависимостями относительно просто, но делать это нужно регулярно. Если автоматизировать хотя бы часть процесса — в долгую выигрыш будет заметным.
Можно проверять каждый пакет вручную, а можно взять taze. Taze показывает, у каких пакетов есть обновления, раскладывает их по semver (major, minor, patch) и умеет применять их автоматически:
pnpm dlx taze -w
Как альтернатива — бот Renovate (или любой похожий). Он сам создает PR и апдейтит package.json.
Честно говоря, мне ближе taze: он работает с pnpm catalogs, и мне нравится после каждого обновления пакета локально проверять сборку — просто чтобы быть уверенным. Но оба варианта, на мой взгляд, вполне ок для прода.
Управление монорепой = Turborepo + pnpm
У монорепозиториев темная история — долгое время это был набор костылей, чтобы заставить все репозитории жить вместе. Сейчас монорепы заметно проще, но все равно сложнее обычного репозитория.
Я создавал и поддерживал много монорепозиториев и считаю Turborepo и pnpm необходимыми для этого.
Честно думаю, что pnpm стоит использовать вместо npm по умолчанию в любом репозитории — он просто быстрее и экономнее по диску. Несколько источников: pnpm vs npm vs yarn vs Bun, I Finally Changed Package Managers, Categorize Your Dependencies. А для монореп pnpm уже давно стал стандартом, тогда как npm только догоняет (например, workspaces появились только в 2020, а catalogs до сих пор нет).
Turborepo — настраиваемая система сборки с кучей фич: кэш сборки (чтобы не пересобирать пакеты воркспейса каждый раз, когда пересобираете фасадное приложение) и центральный оркестратор команд (чтобы, например, прогнать lint по всем воркспейсам).
Версии пакетов = Changesets
Управлять версиями пакетов сложнее, чем кажется на первый взгляд. Нужно внедрить изменение, протестировать, понять, какой это semver-бамп, собрать и зарелизить пакет, обновить changelog. Потом то же самое для следующей версии.
Все усложняется еще сильнее, если хочется батчить изменения перед релизом, автоматически обновлять changelog, корректно считать semver-бамп (когда накопилось несколько изменений разного уровня) — и в итоге сделать это совместной работой команды.
Для этого кейса лучшим инструментом считаю Changesets. Он автоматизирует весь этот процесс. Достаточно на каждое изменение заполнить простую CLI-форму — а дальше релизный пайплайн можно автоматизировать так тонко, как захотите.
Генерация бэкенд-эндпоинтов = OpenAPI + Orval
А что, если хочется полной синхронизации типов и эндпоинтов между бэкендом и фронтендом? В SSR-фреймворках можно взять tRPC или автотипизированный composable useFetch из Nuxt. Но те же приемы не сработают, если у вас классическая SPA: отдельная фронтенд-команда и бэкенд на Java/PHP/чем угодно еще.
Есть GraphQL, но что, если нужен просто типизированный REST-клиент без перехода на GraphQL? Скорее всего, у бэкенда уже есть OpenAPI-документация под Swagger. Через codegen можно взять те же доки и сгенерировать Zod-схемы, типы и даже fetch-сервисы.
Лучший вариант, на мой взгляд, — Orval. Это codegen, который берет OpenAPI-доки и генерирует все вышеперечисленное, плюс удобная документация и простая конфигурация.
Конкретный before / after. На входе — небольшой OpenAPI-спек и конфиг Orval:
# openapi.yaml
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
paths:
/users/{id}:
get:
operationId: getUser
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
required: [id, email]
properties:
id:
type: string
email:
type: string
format: email
// orval.config.ts
import { defineConfig } from 'orval'
export default defineConfig({
api: {
input: './openapi.yaml',
output: {
target: './src/api/endpoints.ts',
client: 'fetch',
schemas: './src/api/models',
},
},
})
После orval получаем типизированные модели и готовый fetch-клиент — без ручного маппинга DTO:
// src/api/models/user.ts (сгенерировано)
export interface User {
id: string
email: string
}
// src/api/endpoints.ts (сгенерировано, упрощено)
export const getUser = async (id: string, options?: RequestInit): Promise<User> => {
const res = await fetch(`/users/${id}`, { ...options, method: 'GET' })
return res.json()
}
Единственная проблема, которую стоит упомянуть: нужно аккуратно выстраивать совместную работу — иначе типы на фронте могут внезапно сломаться (например, вы пишете хотфикс, а бэкенд-команда уже задеплоила фичу на dev, и ваши типы разъехались). Один из вариантов — вынести OpenAPI-схемы в npm-пакет, который обе команды обновляют и версионируют вместе.
Генерация токенов из Figma = Tokens Studio + Style Dictionary
Это одновременно самая полезная и самая сложная в настройке автоматизация — потому что придется менять то, как работает дизайн-команда.
Главная боль при готовом UI — держать его в синхроне с изменениями в Figma. Слышал кучу страшных историй, как команды тратят даже 1/5 спринта только на синхронизацию с Figma. Версионирование в Figma до сих пор слабое, поэтому появляется куча локальных костылей.
Эту проблему можно закрыть через Tokens Studio. По сути это плагин для Figma для управления дизайн-токенами. Когда дизайнеры работают через него, токены можно экспортировать как схему (что-то вроде OpenAPI, но для токенов), а дальше прогнать через Style Dictionary и получить ваш конкретный CSS-сетап — Tailwind, CSS-переменные, Sass и так далее.
Конкретный before / after. Дизайнеры экспортируют схему Tokens Studio, а вы указываете на неё Style Dictionary:
// tokens/colors.json (экспорт из Tokens Studio)
{
"color": {
"brand": {
"primary": { "value": "#2563eb", "type": "color" },
"secondary": { "value": "#7c3aed", "type": "color" }
},
"surface": {
"bg": { "value": "#ffffff", "type": "color" },
"fg": { "value": "#0f172a", "type": "color" }
}
},
"spacing": {
"sm": { "value": "8", "type": "spacing" },
"md": { "value": "16", "type": "spacing" },
"lg": { "value": "24", "type": "spacing" }
}
}
// style-dictionary.config.js
export default {
source: ['tokens/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'build/',
files: [
{
destination: 'theme.css',
format: 'css/variables',
options: {
selector: '@theme',
},
},
],
},
},
}
С selector: '@theme' Style Dictionary сразу генерирует theme-файл для Tailwind v4:
/**
* Do not edit directly, this file was auto-generated.
*/
@theme {
--color-brand-primary: #2563eb;
--color-brand-secondary: #7c3aed;
--color-surface-bg: #ffffff;
--color-surface-fg: #0f172a;
--spacing-sm: 8;
--spacing-md: 16;
--spacing-lg: 24;
}
/* app.css */
@import "tailwindcss";
@import "./build/theme.css";
С версионированием токенов та же история, но Tokens Studio умеет интегрироваться с Git — дизайнеры могут нормально версионировать изменения. Дальше можно сделать npm-пакет, где эта генерация применяется, и закрепить фронтенд на конкретной версии.
Заключение
Я постарался собрать любимые автоматизации с реальным импактом — те, которыми продолжал пользоваться после всех экспериментов. Надеюсь, вы нашли для себя что-то интересное и новое!