У нас уже который год в компании пытаются использовать ОКР для разработки (для каждой команды). По мне это не очень адекватно, т.к.:
Заставляет разработчиков придумывать! ключевые результаты. Что чаще всего выливается в лучшем случае в "сделать x задач в модуле Y", в худшем в абстрактных попугаев в вакууме
Многие результаты зависят от других команд (например, увеличить количество покупок пользователями)
Размывает ответственность. По мне проджект/продакт менеджер должен держать в голове план и понимать путь развития. Т.е. ключевые результаты должны исходить из этого. А когда это делают разработчики, то это очень странно, т.к. они отвечают за реализацию и техническое развитие, а не бизнес стратегии.
Приводит к "одноногим" решениям и техническому долгу. "Ребята, в этом месяце квартале мы будем повышать показатели X". При том, что Y, Z могут полностью провалиться. И на качество решения тоже пофиг, главное ведь X. Не скажешь ведь потом, что мы сделали (X-5) но зато хорошо
Ну т.е. вполне вероятно у нас его неправильно готовят. Но за 3 года работы я никакой пользы не ощутил, кроме митингов по имитации бурной деятельности (придумыванию ОКР и придумыванию, как мы будем показывать, что этот ОКР закрыт).
Как по мне, ОКР конфликтует с законом гутхарда.
В моём видении:
Компания определяет "ключевые" направления развития (чтобы не было сильной расфукосировки)
Определяются основные проблемы, мешающие этому развитию
Рабочее время на X% выделяется на устранение этих проблем, а на 100%-X% на то, что команды решают важным (другие показатели, технические улучшения, аналитику)
Мне кажется орать на коллег за опечатки (вряд ли ещё есть причины для указания цвета в пикселях или использовать неподходящую переменную) это перебор. Я не видел ещё разработчиков, которые не ошибались. А если каждый будет орать друг на друга, так себе атмосфера получится
Не понимаю я этого тона на хабре: "по рукам бить", "орать" и т.п. Если опечатка, то я даже не вижу смысла другого человека тревожить (разве чтобы спросить, не было ли какого тайного умысла в этом).
Если человек наговнокодил, то спокойно рассказать, что не так.
Спасибо за ликбез, вы пожалуй правы. Не стоило мне с неглубокими знаниями пытаться отвечать :)
Но я так понимаю это потенциальная проблема для любого корневого сертификата, что рано или поздно он закончится и старые устройства, которые не обновляются годами, "поломаются"?
Я так понимаю новый корневой сертификат уже доступен какое-то время и те, кто регулярно обновляют свои пользовательские сертификаты, уже подписаны новым корневым.
У меня сертификат валиден 3 месяца, как, думаю, и у большинства. Поэтому нас проблема не коснётся.
Если же это, например, Андроид приложение, куда разработчик добавил старый сертификат, действующий пару лет, то это приложение перестанет работать. И на обновление приложения уйдет какое-то время. Если разработчик его вообще поддерживает ещё
а для REST это новый эндпоинт, как будто это прям проблема проблем
В худшем случае это не один эндпоинт, а набор всевозможных комбинаций под разные экраны на фронте.
У нас количество эндпоинтов, для среднего проекта, около сотни. И большинство из них очень похожи, но отличаются парой лишних связанных полей. Соответственно этот зоопарк постепенно заменим на графкл. Где будет максимум 20 загрузчиков
И добавить не проблема, но меинтейнить (если во внутренних сущностях меняются поля), становится гораздо сложнее
site.ru/api/getUser?id=123&orders=true
Да, это сильно упрощенный вариант графкл, и если этого достаточно, то почему бы и нет. И если таких эндпоинтов несколько штук, то не проблема склеивать всё вручную в каждом эндпоинте.
Но графкл сверху добавляет ещё:
возможность загружать только необходимые поля (тем самым уменьшая трафик и время загрузки)
шареную спецификацию из коробки
удобное версионирование: не обязательно вводить новую версию, можно просто добавлять новые поля когда нужно, а старые удалять, если они не используются
неограниченный уровень вложенности данных (orders=true&order.products=true&order.products.availability=true)
графкл это стандартизированный язык запросов. В то время как предложенный рест вариант это личный велосипед, который сложнее изучить для сложных случаев
В общем, ИМХО, если проект небольшой и у команды с десяток эндпоинтов, при 1-2 связанных сервисах, то скорее всего графкл не нужен и рест будет быстрее.
Если проект покрупнее и вы постоянно занимаетесь склейкой данных для фронта (или других сервисов), то вполне вероятно, что графкл облегчит вам жизнь. Зачастую, кстати, сами фронты и делают промежуточный API gateway сервис, который собирает данные с разных бэкендов и отдаёт их в виде графкл на фронт.
Если рассматривать гибкую, но непроизводительную реализацию графкл:
Для загрузки пользователей с заказами делаются 2 независимых загрузчика:
Загрузка пользователей (по ИД например)
Загрузка заказов, зная пользователей
Когда клиент запрашивает данные, !графкл движок сам! может вызывать или не вызывать эти загрузчики, в зависимости от того что запросил клиент. Например, если запросили только пользователей, то заказы никак не загружаются.
А в РЕСТе пришлось бы реализовывать новый метод.
А теперь представьте, что в некоторых случаях нужна ещё информация по продуктам. Тогда для графкл нужно добавить один загрузчик (продукта по заказам) и всё, дальше умный движок сам будет использовать то что нужно.
А для реста придется добавлять продукты в каждый эндпоинт отдельно.
Как я отметил выше, это может быть неоптимально, т.к. делается несколько запросов в БД (по одному запросу на каждый уровень). Но это не сильно критично в плане производительности и в больших приложениях писать кастомный СКЛ на каждый эндпоинт такое себе удовольствие. Если это так хочется, то есть трансформеры из графкл в скл. Тогда у вас будет ещё меньше работы.
НО у этого обсуждения производительности есть один большой нюанс: графкл это ни разу ни про БД, а про запросы к данным. В общем случае у вас для заказов и продуктов могут быть свои сервисы. БД может быть ноуСКЛ. А какие то данные могут вычисляться на лету. И тогда у вас будет выбор: для РЕСТа каждый раз это загружать вручную и склеивать. Или для графкл написать пару загрузчиков данных (по одному на каждый тип), которые покроют 10-20 разных сценариев.
ПС извините если криво объяснил. Для меня графкл тоже был непонятно зачем, пока не появилась необходимость склеивать данные из разных сервисов.
Представим ситуацию: нам на фронте нужно отобразить имя пользователя и список его заказов. Есть 2 способа:
Сделать апи для загрузки юзера по ИД, и отдельно списка заказов по ИД
Сделать неуниверсальный метод на бэке, который будет отдавать толстый ДТО, содержащий и имя пользователя и список заказов
Первый способ хорош тем, что на бэке не требуется лишней работы. Но дважды дергать сервер и потом всё это склеивать не очень удобно
Второй способ упрощает работу на фронте. Но тогда бэкендеры должны писать кучу скучных маппингов. А фронты ждать нового деплоя. И весь этот код нужно мейнтейнить
Графкл соответственно решает эти проблемы: фронтенд сам решает, что им нужно. И одним запросом на сервер клиент получает необходимые данные. А бэкендеры в этом даже не участвуют (только на изначальном этапе, когда проектируется схема и реализуется загрузка данных для неё)
Кроме того, графкл это ещё и удобная спецификация из коробки, где каждый может видеть, какие поля и типы существуют. И если добавить сверху typescript, получается ещё и с проверкой на компиляции.
У нас, например, фронтенд был мега-счастлив, когда мы им добавили графкл. При том что раньше мы им отдавали толстые ДТО, т.е. у них работы не сильно убавилось. Главное это независимость и контроль над тем, что они делают.
Обычно я в команде главный противник (чрезмерных) моков, потому что тесты слишком раздутые получаются и хрупкие. К тому же обычно никто не тестирует взаимодействие реальных компонентов, только их в отдельности.
В статье чуть лучше описано обоснование для моих аргументов
а в Java так не писали вообще никогда (Java'вский import ничего не подгружает, а работает как alias, можно и без import'ов, если указывать полные имена классов)
По-моему в джаве только так и пишут. Можно указывать полные имена, да, но так мало кто делает, кроме случаев совпадающих имен
Т.е. по факту основная претензия: в JS нельзя использовать полные имена вместо импорта, что почти никому и не нужно
Касаемо необходимости экспорта: вам уже написали, что это просто инкапсуляция. Если не хочется иметь инкапсуляцию и хочется открыть всё, есть вебпак плагин, который сможет все поля/методы/классы экспортировать за вас.
Пс, в джаве с версии 9 тоже есть возможность/необходимость указывать экспорты для модулей. Если бы не backward compatibilty, возможно это сделали бы настройкой по умолчанию.
Запуск скрипта/программы требует подходящего окружения, иначе толку с него, если он запускается только у вас.
Например, в подходящее окружение входят:
ОС
зависимости, например на питоновские либы или 3rd party tools (например на тот же bash)
таймзоны
Докер все эти проблемы и решает.
Языки (компилируемые или нет) не могут решить эти проблемы и/или требуют чрезмерного усложнения скрипта. Компилируемые языки тут не сильно помогают.
Да, ненативная компиляция (например java/python), может увеличивать количество проблем с окружением. Но в то же время не требует создавать бинарники под каждую ОС.
Я соглашусь с вами, что докер для запуска скриптов, это чаще всего нехилый и ненужный оверхед. Но и не обязательно это делать каждый раз. Важно знать, что такой подход существует и может быть оправдан в некоторых случаях.
Когда изредка приходится пользоваться виндой, обхожусь без них с большим неудобством :)
И сотня команд по сравнению с миллионнами всех программ это капля в море.
Чаще всего софт делают для винды/мака. А на линуксе альтернативы.
И чаще всего это вопрос не языка, а именно адаптации под ОС
Какие программы являются настоящими? И какие языки?
Я как пользователя линукса довольно редко встречаю "настоящие" программы. Или линукс тоже ненастоящая ОС?
От себя хотелось бы добавить ещё 2 способа защиты:
В некоторых языках (например Java и Python), при вызове команды есть 2 перегруженых метода: первый принимает строку с полной командой, а второй — массив, где первый аргумент это команда, а последующие аргументы это аргументы для конкретно это команды (например ["echo", "123", "|", "rm", "-rf", "/"] просто напечатает все переданные аргументы). Таким образом, если у вас нет 100% контроля над входными данными, то лучше использовать второй способ.
Когда работаете со скриптами, не забывайте оборачивать переменные в кавычки:
Дело вкуса. На мой взгляд, если убрать бесполезные булевы переменные и оставить только константы (кроме очевидных ноября и декабря), для меня код будет более читабельным, чем комментарии.
PAYOUT_APPROVAL_DATE = date(2020, 5, 1)
BANNED_COUNTRIES = ['Narnia', 'Odan', 'Maldonia']
ELIGIBLE_MONTHS = [11, 12]
today = date.today()
if (today > PAYOUT_APPROVAL_DATE)
and (today.month in ELIGIBLE_MONTHS)
and (not user.country in BANNED_COUNTRIES):
stripe_payout_resp = callStripeToPayout(user)
saveResponseAsync(stripe_payout_resp)
Кроме того, на своей практике встречался с таким количеством устаревших комментариев, что у меня на все комментарии выработался автроматический фильтр, как на рекламу в интернете. И я бы предпочел, чтобы разработчики тратили больше времени на адекватные названия, чем на развёрнутые комментарии.
Разумеется есть случаи, когда без комментариев трудно обойтись, как в примере статьи с race condition. Ну и кому-то код читать сложнее, чем комментарии. Так что это задача без правильного ответа. Главное, чтобы в команде все были на одной волне, и на код ревью такие вещи улучшались.
Честно говоря, мой посыл был в другом: мне хочется, чтобы начинающие разработчики, прочитав эту статью, прочитали и мой комментарий. И вместо того, чтобы городить свой огород с хуками и локальными решениями, использовали более популярные стандарты. Это облегчает работу другим разработчикам (и даже себе будущему)
Если у автора всё работает, он всё делает с одного компа, и никто его код трогать не будет, то решение себя оправдывает. Конечно, ничего переделывать не нужно.
Но, возможно, задуматься о более подходящих инструментах для новых проектов. Если их использование не сопряжено с чрезмерными трудностями.
ПС "я не сцу кипятком от слова CI" тоже. Для меня CI = автоматизация, и вот она действительно важна. Решение с хуками, как по мне, это недо-автоматизация.
ППС я тоже начинал с ручных поделок и хуков (да и сейчас они есть). Но единственная причина их иметь, как по мне, это лень и чрезмерное количество свободного времени, чтобы тратить его вручную
У нас уже который год в компании пытаются использовать ОКР для разработки (для каждой команды). По мне это не очень адекватно, т.к.:
Заставляет разработчиков придумывать! ключевые результаты. Что чаще всего выливается в лучшем случае в "сделать x задач в модуле Y", в худшем в абстрактных попугаев в вакууме
Многие результаты зависят от других команд (например, увеличить количество покупок пользователями)
Размывает ответственность. По мне проджект/продакт менеджер должен держать в голове план и понимать путь развития. Т.е. ключевые результаты должны исходить из этого. А когда это делают разработчики, то это очень странно, т.к. они отвечают за реализацию и техническое развитие, а не бизнес стратегии.
Приводит к "одноногим" решениям и техническому долгу. "Ребята, в этом месяце квартале мы будем повышать показатели X". При том, что Y, Z могут полностью провалиться. И на качество решения тоже пофиг, главное ведь X. Не скажешь ведь потом, что мы сделали (X-5) но зато хорошо
Ну т.е. вполне вероятно у нас его неправильно готовят. Но за 3 года работы я никакой пользы не ощутил, кроме митингов по имитации бурной деятельности (придумыванию ОКР и придумыванию, как мы будем показывать, что этот ОКР закрыт).
Как по мне, ОКР конфликтует с законом гутхарда.
В моём видении:
Компания определяет "ключевые" направления развития (чтобы не было сильной расфукосировки)
Определяются основные проблемы, мешающие этому развитию
Рабочее время на X% выделяется на устранение этих проблем, а на 100%-X% на то, что команды решают важным (другие показатели, технические улучшения, аналитику)
Мне кажется орать на коллег за опечатки (вряд ли ещё есть причины для указания цвета в пикселях или использовать неподходящую переменную) это перебор. Я не видел ещё разработчиков, которые не ошибались. А если каждый будет орать друг на друга, так себе атмосфера получится
Не понимаю я этого тона на хабре: "по рукам бить", "орать" и т.п. Если опечатка, то я даже не вижу смысла другого человека тревожить (разве чтобы спросить, не было ли какого тайного умысла в этом).
Если человек наговнокодил, то спокойно рассказать, что не так.
Добрее надо быть
У меня коллега британка всегда говорит "Pardon me".
Спасибо за ликбез, вы пожалуй правы. Не стоило мне с неглубокими знаниями пытаться отвечать :)
Но я так понимаю это потенциальная проблема для любого корневого сертификата, что рано или поздно он закончится и старые устройства, которые не обновляются годами, "поломаются"?
Я так понимаю новый корневой сертификат уже доступен какое-то время и те, кто регулярно обновляют свои пользовательские сертификаты, уже подписаны новым корневым.
У меня сертификат валиден 3 месяца, как, думаю, и у большинства. Поэтому нас проблема не коснётся.
Если же это, например, Андроид приложение, куда разработчик добавил старый сертификат, действующий пару лет, то это приложение перестанет работать. И на обновление приложения уйдет какое-то время. Если разработчик его вообще поддерживает ещё
В худшем случае это не один эндпоинт, а набор всевозможных комбинаций под разные экраны на фронте.
У нас количество эндпоинтов, для среднего проекта, около сотни. И большинство из них очень похожи, но отличаются парой лишних связанных полей. Соответственно этот зоопарк постепенно заменим на графкл. Где будет максимум 20 загрузчиков
И добавить не проблема, но меинтейнить (если во внутренних сущностях меняются поля), становится гораздо сложнее
Да, это сильно упрощенный вариант графкл, и если этого достаточно, то почему бы и нет. И если таких эндпоинтов несколько штук, то не проблема склеивать всё вручную в каждом эндпоинте.
Но графкл сверху добавляет ещё:
возможность загружать только необходимые поля (тем самым уменьшая трафик и время загрузки)
шареную спецификацию из коробки
удобное версионирование: не обязательно вводить новую версию, можно просто добавлять новые поля когда нужно, а старые удалять, если они не используются
неограниченный уровень вложенности данных (orders=true&order.products=true&order.products.availability=true)
графкл это стандартизированный язык запросов. В то время как предложенный рест вариант это личный велосипед, который сложнее изучить для сложных случаев
В общем, ИМХО, если проект небольшой и у команды с десяток эндпоинтов, при 1-2 связанных сервисах, то скорее всего графкл не нужен и рест будет быстрее.
Если проект покрупнее и вы постоянно занимаетесь склейкой данных для фронта (или других сервисов), то вполне вероятно, что графкл облегчит вам жизнь. Зачастую, кстати, сами фронты и делают промежуточный API gateway сервис, который собирает данные с разных бэкендов и отдаёт их в виде графкл на фронт.
Если рассматривать гибкую, но непроизводительную реализацию графкл:
Для загрузки пользователей с заказами делаются 2 независимых загрузчика:
Загрузка пользователей (по ИД например)
Загрузка заказов, зная пользователей
Когда клиент запрашивает данные, !графкл движок сам! может вызывать или не вызывать эти загрузчики, в зависимости от того что запросил клиент. Например, если запросили только пользователей, то заказы никак не загружаются.
А в РЕСТе пришлось бы реализовывать новый метод.
А теперь представьте, что в некоторых случаях нужна ещё информация по продуктам. Тогда для графкл нужно добавить один загрузчик (продукта по заказам) и всё, дальше умный движок сам будет использовать то что нужно.
А для реста придется добавлять продукты в каждый эндпоинт отдельно.
Как я отметил выше, это может быть неоптимально, т.к. делается несколько запросов в БД (по одному запросу на каждый уровень). Но это не сильно критично в плане производительности и в больших приложениях писать кастомный СКЛ на каждый эндпоинт такое себе удовольствие. Если это так хочется, то есть трансформеры из графкл в скл. Тогда у вас будет ещё меньше работы.
НО у этого обсуждения производительности есть один большой нюанс: графкл это ни разу ни про БД, а про запросы к данным. В общем случае у вас для заказов и продуктов могут быть свои сервисы. БД может быть ноуСКЛ. А какие то данные могут вычисляться на лету. И тогда у вас будет выбор: для РЕСТа каждый раз это загружать вручную и склеивать. Или для графкл написать пару загрузчиков данных (по одному на каждый тип), которые покроют 10-20 разных сценариев.
ПС извините если криво объяснил. Для меня графкл тоже был непонятно зачем, пока не появилась необходимость склеивать данные из разных сервисов.
Представим ситуацию: нам на фронте нужно отобразить имя пользователя и список его заказов. Есть 2 способа:
Сделать апи для загрузки юзера по ИД, и отдельно списка заказов по ИД
Сделать неуниверсальный метод на бэке, который будет отдавать толстый ДТО, содержащий и имя пользователя и список заказов
Первый способ хорош тем, что на бэке не требуется лишней работы. Но дважды дергать сервер и потом всё это склеивать не очень удобно
Второй способ упрощает работу на фронте. Но тогда бэкендеры должны писать кучу скучных маппингов. А фронты ждать нового деплоя. И весь этот код нужно мейнтейнить
Графкл соответственно решает эти проблемы: фронтенд сам решает, что им нужно. И одним запросом на сервер клиент получает необходимые данные. А бэкендеры в этом даже не участвуют (только на изначальном этапе, когда проектируется схема и реализуется загрузка данных для неё)
Кроме того, графкл это ещё и удобная спецификация из коробки, где каждый может видеть, какие поля и типы существуют. И если добавить сверху typescript, получается ещё и с проверкой на компиляции.
У нас, например, фронтенд был мега-счастлив, когда мы им добавили графкл. При том что раньше мы им отдавали толстые ДТО, т.е. у них работы не сильно убавилось. Главное это независимость и контроль над тем, что они делают.
Если у вас простое круд приложение, без интеграции и функционала, то да.
Помимо БД есть, например:
Аутентификация/авторизация
Валидация
Интеграция с другими сервисами
Отправка сообщений пользователям (сис, имейл и т.п.)
Регулярно повторяемые события
Экспорт данных
И многое многое другое
К тому же, зачастую привязывать графкл к БД это плохая затея, т.к. уровень связности кода очень высок.
Спасибо за статью.
Обычно я в команде главный противник (чрезмерных) моков, потому что тесты слишком раздутые получаются и хрупкие. К тому же обычно никто не тестирует взаимодействие реальных компонентов, только их в отдельности.
В статье чуть лучше описано обоснование для моих аргументов
Следующий шаг: по-умолчанию считать всех детьми. И каждый гражданин будет обязан доказывать через 7 кругов ада, что он не ребёнок
Не зря ведь недавно увеличили возраст молодёжи
По-моему в джаве только так и пишут. Можно указывать полные имена, да, но так мало кто делает, кроме случаев совпадающих имен
Т.е. по факту основная претензия: в JS нельзя использовать полные имена вместо импорта, что почти никому и не нужно
Касаемо необходимости экспорта: вам уже написали, что это просто инкапсуляция. Если не хочется иметь инкапсуляцию и хочется открыть всё, есть вебпак плагин, который сможет все поля/методы/классы экспортировать за вас.
Пс, в джаве с версии 9 тоже есть возможность/необходимость указывать экспорты для модулей. Если бы не backward compatibilty, возможно это сделали бы настройкой по умолчанию.
Запуск скрипта/программы требует подходящего окружения, иначе толку с него, если он запускается только у вас.
Например, в подходящее окружение входят:
Докер все эти проблемы и решает.
Языки (компилируемые или нет) не могут решить эти проблемы и/или требуют чрезмерного усложнения скрипта. Компилируемые языки тут не сильно помогают.
Да, ненативная компиляция (например java/python), может увеличивать количество проблем с окружением. Но в то же время не требует создавать бинарники под каждую ОС.
Я соглашусь с вами, что докер для запуска скриптов, это чаще всего нехилый и ненужный оверхед. Но и не обязательно это делать каждый раз. Важно знать, что такой подход существует и может быть оправдан в некоторых случаях.
Когда изредка приходится пользоваться виндой, обхожусь без них с большим неудобством :)
И сотня команд по сравнению с миллионнами всех программ это капля в море.
Чаще всего софт делают для винды/мака. А на линуксе альтернативы.
И чаще всего это вопрос не языка, а именно адаптации под ОС
Какие программы являются настоящими? И какие языки?
Я как пользователя линукса довольно редко встречаю "настоящие" программы. Или линукс тоже ненастоящая ОС?
https://youtu.be/5seefpjMQJI вот симуляция посадки на Марс от самих спейс икс.
Как это в реальности будет война покажет, но план таков
От себя хотелось бы добавить ещё 2 способа защиты:
["echo", "123", "|", "rm", "-rf", "/"]просто напечатает все переданные аргументы). Таким образом, если у вас нет 100% контроля над входными данными, то лучше использовать второй способ.Дело вкуса. На мой взгляд, если убрать бесполезные булевы переменные и оставить только константы (кроме очевидных ноября и декабря), для меня код будет более читабельным, чем комментарии.
Кроме того, на своей практике встречался с таким количеством устаревших комментариев, что у меня на все комментарии выработался автроматический фильтр, как на рекламу в интернете. И я бы предпочел, чтобы разработчики тратили больше времени на адекватные названия, чем на развёрнутые комментарии.
Разумеется есть случаи, когда без комментариев трудно обойтись, как в примере статьи с race condition. Ну и кому-то код читать сложнее, чем комментарии. Так что это задача без правильного ответа. Главное, чтобы в команде все были на одной волне, и на код ревью такие вещи улучшались.
Выглядит на первый взгляд неплохо. Но как стал пользоваться обнаружил сходу несколько неудобных моментов. У вас есть bug/issue tracker?
Честно говоря, мой посыл был в другом: мне хочется, чтобы начинающие разработчики, прочитав эту статью, прочитали и мой комментарий. И вместо того, чтобы городить свой огород с хуками и локальными решениями, использовали более популярные стандарты. Это облегчает работу другим разработчикам (и даже себе будущему)
Если у автора всё работает, он всё делает с одного компа, и никто его код трогать не будет, то решение себя оправдывает. Конечно, ничего переделывать не нужно.
Но, возможно, задуматься о более подходящих инструментах для новых проектов. Если их использование не сопряжено с чрезмерными трудностями.
ПС "я не сцу кипятком от слова CI" тоже. Для меня CI = автоматизация, и вот она действительно важна. Решение с хуками, как по мне, это недо-автоматизация.
ППС я тоже начинал с ручных поделок и хуков (да и сейчас они есть). Но единственная причина их иметь, как по мне, это лень и чрезмерное количество свободного времени, чтобы тратить его вручную