15 лет в технологиях: что создание приложений в 2010-2014 до сих пор нас учит
В 2010 году мы писали PhoneGap-приложения в UIWebView. В 2011 году выпустили первый Node.js-сервис - версия 0.6, без LTS, PM2 ещё в бете. В 2012 году спорили с клиентами о mobile-first вайрфреймах. В 2013 году запускали Hadoop-кластеры для датасетов, которые сегодня поместились бы в Postgres. В 2014 году портировали Flash-анимации на Canvas API и называли это «будущим веба».
Большинство того конкретного стека ушло в прошлое или стало неузнаваемым. Но уроки от навигации по нему - что оптимизировать, куда ставить, когда держаться - мы применяем в каждом проекте, который строим сейчас. Не как ностальгию. Как распознавание паттернов.
Вот что действительно осталось.
1. Урок pull-vs-push никогда не устаревает
В 2011 году у нас был дашборд, где 150 пользователей опрашивали PHP-сервер каждые 5 секунд. 1800 MySQL-запросов в минуту, 98% возвращали идентичные данные. Мы заменили цикл опроса на Node.js WebSockets и сократили запросы к базе данных в 2700 раз.
Конкретные технологии - Socket.io 0.9, обнаружение изменений MySQL через интервальный опрос - устарели. Но архитектурное понимание вечно: pull-модель для push-природных данных - это потеря по архитектурному замыслу.
Мы видим, как этот паттерн проваливается в 2025 в другом обличье:
- Мобильные приложения, опрашивающие REST API каждые 30 секунд для изменений состояния, происходящих раз в час
- Дашборды, обновляющиеся по таймеру, когда могли бы подписаться на pub/sub-канал
- Микросервисы, опрашивающие таблицу базы данных для очередей задач вместо использования event bus
Исправление всегда то же, что в 2011: определи, являются ли изменения данных событийными или временны́ми, и подбери модель доставки соответственно. Server-Sent Events, WebSockets, вебхуки, SQS - вариантов сейчас больше. Рассуждение, которое к ним приводит, не изменилось.
2. Урок mobile-first - о порядке принятия решений, а не об экранах
В 2012 году мы перевернули процесс проектирования: сначала мобильный вайрфрейм, потом десктопный макет. Техническая причина - размер экрана. Настоящая причина - принудительная приоритизация до того, как кто-либо привязался к конкретному макету.
Десктопный холст даёт место для откладывания сложных решений. Можно добавить сайдбар, вторичную навигацию, рекламный баннер, ленту соцсетей. К моменту перехода на мобильный у тебя есть дизайн, который клиент полюбил, но не помещающийся в 320px. Ты отнимаешь вещи у дизайна, к которому люди уже эмоционально привязаны. Этот разговор всегда болезненный.
Mobile-first задавал вопрос рано: что одно нужно сделать пользователю на этой странице? Всё остальное было необязательным.
В 2025 году аналогичный паттерн постоянно появляется в продуктовых решениях. Мы научились задавать его до написания строчки кода:
- Что одно должна делать эта фича?
- Что можно убрать, не потеряв это?
- Что мы добавляем потому что есть место, а не потому что нужно?
Ограничение, делающее мобильный дизайн хорошим - ограниченное пространство вынуждает к ясности - это то же ограничение, которое делает хорошим продуктовый скоупинг. Ограниченные спринты вынуждают к приоритизации. Mobile-first мышление применимо к роадмапам.
3. Новые рантаймы: всегда деплоить low-stakes первым
Наш деплой Node.js 0.6 в production в 2011 году имел один OOM-инцидент за 60 дней - утечку памяти в lastDataHash, проявившуюся через 18 дней. PM2 автоперезапустился за 2 секунды. Никто не был оповещён. Дашборд ненадолго опустел.
Нам повезло. Но мы также проектировали под удачу: запустили Node.js за nginx с менеджером процессов, обильно логировали всё, и выбрали BI-дашборд - не чекаут - в качестве первой production-нагрузки.
Принцип: при принятии незнакомого рантайма или фреймворка выбирай первый production-деплой по радиусу поражения, а не по энтузиазму.
Мы до сих пор следуем этому правилу для каждой новой технологии:
- Первый деплой AWS Lambda был генератором фоновых отчётов, а не API-эндпоинтом
- Первый Rust-сервис был офлайн-батч-процессором, а не пользовательским путём
- Первый экран React Native был страницей настроек, а не основным продуктовым флоу
Новая технология в low-stakes контексте даёт: реальные production-данные, реальные режимы отказов, реальные показатели производительности, и уверенность (или свидетельства против) использования её в более высокорисковых контекстах. Это ценнее любого бенчмарка или туториала.
4. Абстракции стареют быстрее, чем проблемы
В 2012-2013 годах шли войны JavaScript MVC: Backbone.js против Knockout.js против Ember.js против Angular. Backbone давал структуру; Knockout - двустороннее связывание; Angular - весь фреймворк целиком. У нас были мнения о том, что лучше. Сильные мнения.
К 2014 году существовал React. К 2016-му он победил. К 2020-му модель хуков заменила модель классовых компонентов. Каждый фреймворк, о котором мы спорили в 2012 году, либо исчез, либо стал неузнаваемым.
Проблемы, которые эти фреймворки решали - управление UI-состоянием, синхронизация данных с DOM, структурирование JavaScript-приложений - это те же самые проблемы, которые сегодня решают React, Vue и Svelte.
Урок: вкладывай понимание в проблему, а не в фреймворк. Когда разработчик, понимавший зачем существовал Backbone - проблему синхронизации состояния в масштабе - переходил на React, кривая обучения была неделей. Когда разработчик, зазубривший API Backbone, переходил на React, это был месяц разочарований.
Мы теперь проверяем это на собеседованиях. «Расскажи о технологии, которую ты использовал и которая больше не актуальна. Что ты усвоил, что перенёс дальше?» Ответ полезнее любого теста на знание фреймворка.
5. 80% точности выпущенное лучше 95% запланированного
В 2013 году мы выпустили OCR-сканер чеков с 68% точностью символов. После предобработки - 81%. С UI подтверждения пользователем это было полезно. Люди пользовались. Тысячи авансовых отчётов прошли через него.
Мы могли ждать лучшего OCR. Google Cloud Vision API давал 95%+ точности - в 2016 году. Если бы мы ждали 95%, то не выпустили бы ничего три года, ничего не узнали бы о реальных вариациях чеков, и не имели бы пользовательских данных, чтобы обосновать инвестиции, когда Vision API появился.
Продукт, выпущенный при 81% + подтверждение пользователем, был лучше, чем продукт, не выпущенный при 95% точности.
Мы постоянно применяем этот фрейминг к AI/ML-фичам в 2025:
- Выпусти рекомендательную систему с простой коллаборативной фильтрацией и посмотри, вовлекаются ли пользователи, прежде чем строить трансформер-модель
- Выпусти поиск по ключевым словам перед семантическим поиском; разница в точности часто меньше, чем разница в сложности продукта
- Выпусти модерацию контента на правилах перед LLM-модерацией; настраивай на реальных данных, потом обновляй
Решение на 80% говорит тебе, каким должно быть решение на 95%. Невыпущенное решение на 95% не говорит ничего.
6. Стоимость операционной сложности всегда недооценивается
В 2012-2013 годах «большие данные» означали, что тебе, наверное, стоит запустить Hadoop. Мы запустили Hadoop-кластер для проекта аналитики контента с ~50 ГБ логов. Hadoop был спроектирован для петабайт на сотнях узлов. У нас было 4 узла.
Операционные накладные расходы - обслуживание кластера, планирование задач, отладка сбоев MapReduce, проблемы репликации HDFS - были постоянными. Данные, правильно проиндексированные в Postgres, отвечали бы на каждый вопрос, который мы задавали через Hadoop, и тратили бы в 10 раз меньше времени на запрос.
Мы выбрали инструмент для проблемы, которую воображали иметь, а не для проблемы, которая была на самом деле.
В 2025 году тот же паттерн провала появляется с микросервисами, Kubernetes, распределённой трассировкой, event sourcing и CQRS. Всё это - легитимные инструменты для реальных проблем в масштабе. Все они чрезмерно применяются на уровне стартапа.
Вопросы, которые мы теперь задаём перед добавлением инфраструктурной сложности:
- Каков реальный объём данных, объём запросов или размер команды, которые это делают необходимым?
- Каковы операционные затраты после запуска - мониторинг, отладка, деплой?
- Какая более простая альтернатива существует, от которой нам пришлось бы вырасти, прежде чем это понадобится?
«Может, понадобится позже» - не ответ. «При масштабе» = когда у тебя есть проблема, а не когда ты её предвидишь.
7. Ментальная модель пользователя - это настоящий продукт
В 2014 году мы портировали Flash-конфигуратор продуктов на HTML5 Canvas. Оригинальная Flash-версия создавалась годами и была действительно впечатляющей - анимированный 3D-рендеринг продукта, выбор компонентов drag-and-drop, расчёт цены в реальном времени.
Мы пересоздали основное взаимодействие на HTML5 Canvas + requestAnimationFrame. Работало на 60fps в современных браузерах, работало на iPad, загружалось без плагинов. Технически превосходило во всём.
Два месяца пользователи ненавидели это.
Не потому что было медленнее или менее функционально. Потому что выглядело по-другому. Позиции кнопок слегка сдвинулись. Тайминг анимации изменился. Курсор менялся в других точках. У пользователей, работавших с Flash-версией годами, была мышечная память для взаимодействия, и мы её сломали.
Мы перестроили точно под интерактивную модель Flash-версии - те же области клика, тот же тайминг анимации, те же звуковые подтверждения - и принятие восстановилось.
Урок: пользователи не испытывают твою архитектуру. Они испытывают свою ментальную модель твоего продукта. Техническая миграция - это не продуктовая миграция. Ты можешь поменять каждый слой стека и сохранить опыт продукта, или сохранить старый стек и случайно сломать опыт. Это два независимых процесса.
Это самая распространённая ошибка в «редизайнах» и «модернизациях», которые мы видим в 2025 году. Команды пересоздают инфраструктуру, переходят на новые фреймворки, переписывают на TypeScript и называют это «без изменений для пользователей». Потом пользователи замечают, что что-то кажется не так, даже если не могут сказать что именно.
Перед любым значительным пересозданием задокументируй интерактивную модель, которую сохраняешь. Не UI. Ментальную модель - где что находится, что делают вещи, что означает обратная связь. Потом проверь, что сохранил её, прежде чем выпускать.
8. Срок жизни «лучших практик» становится короче
В 2010 году лучшей практикой для JavaScript было объединить все скрипты в один файл и минифицировать. В 2015-м - Webpack-модули. В 2020-м - разделение кода и ленивая загрузка. В 2024-м - RSC со стримингом и edge-рендерингом.
Правильный ответ менялся пять раз за 14 лет. Каждый ответ был правильным в своём контексте: условия сети, возможности браузеров, зрелость инструментов, размер приложения. «Лучшая практика» всегда была «лучшей при текущих ограничениях».
Что не меняется: лежащее в основе рассуждение. Минимизировать передаваемые байты. Минимизировать блокирующую рендеринг работу. Парсить то, что нужно, когда это нужно. Каждый правильный ответ с 2010 по 2024 год - это применение тех же принципов к разным ограничениям.
Мы перестали изучать «текущую лучшую практику» как конечную точку и начали изучать рассуждение, порождающее лучшие практики. Конечная точка изменится. Рассуждение переносится.
Когда кто-то в команде спрашивает «является ли X правильным подходом?», ответ, который мы ищем, - не «да, X является текущей лучшей практикой». А: «вот ограничения, которые оптимизирует X, и вот применимы ли эти ограничения к нам».
9. Измерение меняет то, что ты строишь
В 2012 году, до внедрения mobile-first, у нас были мнения о мобильном UX. После измерения - 18% мобильного трафика, 68% показатель отказов на мобильном, 0.8% конверсия на мобильном против 4.1% на десктопе - у нас был мандат.
Измерение не говорило нам, что строить. Оно говорило нам о цене невыполнения. Это другой разговор с клиентом, другое обоснование трёх недель редизайна, другой приоритет в спринте.
Каждое значительное решение, которое мы принимали и которое работало, имело измерение в центре. Не всегда точное - иногда направленное, иногда прокси-метрики - но что-то, делавшее компромисс читаемым.
Дисциплина, выстроенная с 2012 года: перед реализацией чего-либо значительного определи, как выглядит «работает» в числах. Не «пользователям понравится». Не «будет ощущаться лучше». Какое число вырастет, насколько, за какой срок? Если не можешь определить это до построения, не узнаешь, успешно ли ты после.
Это звучит очевидно. На практике большинство решений по фичам принимаются на интуиции и одобряются ретроспективными метриками. Фича выпущена, метрика, которую мы отслеживали, выросла по другим причинам, мы назвали это победой. Правильное измерение - определить сначала, измерять против определения - всё ещё редкость.
10. Сложные проценты скучной инфраструктуры
В 2012-2014 мы построили первый настоящий CI/CD-пайплайн: Jenkins, автоматические тесты, staging-окружение, деплой одной командой. Настройка заняла четыре недели. Казалось накладными расходами.
За следующие два года он окупился в каждой фиче, которую мы выпускали быстрее, каждом баге, пойманном до production, каждом пятничном вечере, который мы не тратили на ручной деплой и последующий мониторинг сбоев.
Конкретные инструменты - Jenkins, а не GitHub Actions; физические staging-серверы, а не Heroku review apps - устарели. Логика инвестиций идентична той, что мы применяем в 2025 году.
Каждая «скучная» инфраструктурная инвестиция, автоматизирующая ручной процесс, накапливается со временем. Каждый ручной процесс, который ты нормализуешь - «мы просто делаем это вручную, занимает только 20 минут» - накапливается в часы за спринт, потом в дни за квартал, потом в итоговые инциденты, когда кто-то забывает шаг.
Вопрос, который мы теперь задаём, когда любой процесс выполняется вручную более двух раз: сколько стоит автоматизация, сколько стоит неавтоматизация за 12 месяцев? Ответ почти всегда: автоматизировать.
Чем на самом деле была эпоха 2010-2014
Мы иногда романтизируем её - дикий запад, выпуск вещей, едва работавших, открытие паттернов, ставших учебниковыми. Реальность также включала: много потраченных усилий на инструменты, неправильные для задачи, решения с недостаточными данными, пользовательские опыты, страдавшие, пока мы учились в production.
Настоящим переносимым оказалось не какая-то конкретная технология или паттерн. А привычка понимать почему было принято решение - какое ограничение оно решало, какой режим отказа вводило, что нужно было бы изменить, чтобы решение оказалось неправильным.
Node.js решал проблему C10K для I/O-ориентированных сервисов. Это всё ещё реальное ограничение. Модель event loop всё ещё правильный ответ для этого ограничения. Конкретная версия Node.js и окружающая экосистема изменились полностью.
Mobile-first решал проблему откладывания приоритизации в процессе проектирования. Это всё ещё реальное ограничение. Начало дизайна с ограничений всё ещё правильный ответ. Конкретные ограничения - экраны 320px, соединения 3G - сместились, но принцип не изменился.
Каждый урок из 2010-2014, который мы до сих пор используем, имеет такую форму: проблема, ограничение и паттерн, адресующий ограничение. Убери специфику - получишь переносимую модель. Оставь только специфику - получишь ностальгию.
Мы до сих пор учимся из проектов, работавших на серверах, которые давно выведены из эксплуатации. Код ушёл. Понимание почему он работал - или нет - работает каждый день.