Коллеги, всем привет!
Давайте честно: сколько из вас за последний год успели если не впрогреть, то хотя бы посмотреть в сторону нового JS-фреймворка? Qwik, Solid, Svelte — список можно продолжать. Я сам постоянно ловлю себя на этом: вижу хайп, читаю доки, даже пет-проект пытаюсь сделать... А потом прихожу на работу и видим тот же легаси-код, который всё сложнее поддерживать.
И тут я задал себе простой вопрос: а что, если дело не в фреймворках? Что, если мы просто лечим симптомы, а не болезнь?
Болезнь под названием "хочу новый фреймворк"
Знакомо:
Начинаешь новый проект — руки чешутся взять что-то модное
Через полгода код превращается в спагетти, даже с самым крутым фреймворком
Рефакторинг становится болью
Тесты писать сложно
Команда боится вносить изменения
Я прошел через это. Потратил кучу времени на изучение новых инструментов, но проблемы оставались. Пока не понял одну простую вещь...
Лекарство: три принципа, которые перевернули мой подход
Принцип 1: Делай одну вещь, но делай её хорошо
Помните тот ужасный компонент, который:
Ходит за данными
Форматирует их
Рендерит
И ещё аналитику отправляет?
Я называл это "компонент-бог". И таких у меня было полно. Пока не попробовал разбивать всё на мелкие сущности с одной ответственностью.
Вот как это выглядит сейчас:
userApiService.js → только запросы к API
userDataMapper.js → преобразование данных
AnalyticsTracker.js → только аналитика
UserProfileView.js → только отображение
Да, файлов стало больше. Зато теперь:
Если падает API — я знаю, где искать
Если дизайнер поменял вёрстку — правлю один файл
Если нужно добавить аналитику — не трогаю компонент
Принцип 2: Не прячь зависимости — покажи их
Раньше я постоянно импортил axios прямо в компонентах. Казалось, что так быстрее. Пока не попробовал написать тесты для такого кода...
Теперь я явно передаю все зависимости:
// Было
const UserList = () => {
useEffect(() => {
axios.get('/api/users').then(...)
}, [])
}
// Стало
const UserList = ({ userService }) => {
useEffect(() => {
userService.getUsers().then(...)
}, [userService])
}
Разница кажется небольшой, но на практике:
Тесты пишутся в 3 раза быстрее
Легко подменить реализацию (например, для моков)
Код становится предсказуемым
Принцип 3: Завись от интерфейсов, а не от реализаций
Это звучало для меня как какая-то магия, пока я не попробовал на практике.
Вместо того чтобы компонент знал про конкретный API:
// Плохо: компонент знает слишком много
const UserList = () => {
const [users, setUsers] = useState([])
useEffect(() => {
fetch('/api/v2/users') // А если бэкенд поменяет API?
.then(response => response.json())
.then(setUsers)
}, [])
}
Я создаю прослойку-абстракцию:
// userRepository.js
class UserRepository {
async getUsers() {
// Здесь может быть REST, GraphQL, моки - не важно
const response = await fetch('/api/users')
return response.json()
}
}
// В компоненте
const UserList = ({ userRepository }) => {
const [users, setUsers] = useState([])
useEffect(() => {
userRepository.getUsers().then(setUsers)
}, [userRepository])
}
Теперь если бэкенд поменяет API, мне нужно поправить всего один файл, а не 15 компонентов.
Что изменилось в моей работе
После того как я начал применять эти принципы:
Скорость разработки сначала немного упала (привыкал к новому подходу), но потом выросла в разы
Качество кода стало заметно лучше — коллеги теперь понимают мой код с полуслова
Рефакторинг перестал быть болью
Тесты пишутся легко и быстро
Да, я всё ещё смотрю в сторону новых фреймворков. Но теперь я понимаю: не важно, React у меня или SolidJS — эти принципы работают везде.
Выводы, которые я сделал
Фреймворки приходят и уходят, а архитектурные принципы остаются
Инвестиция в архитектуру окупается быстрее, чем кажется
Простота важнее модных фич
Я не призываю вас забросить изучение нового. Просто предлагаю сначала построить прочный фундамент.
А какие архитектурные принципы помогают вам в работе? Давайте обсудим в комментариях — возможно, я упустил что-то важное!