00:00:00[МУЗЫКА]
00:00:21>> Всем привет, добрый день.
00:00:22Я начну свое выступление с небольшого признания.
00:00:26Я выпускал код, который не до конца понимал.
00:00:29Генерировал его, тестировал, внедрял, но не мог объяснить, как он работает.
00:00:33И вот в чем штука: готов поспорить, что каждый из вас делал так же.
00:00:37>> [СМЕХ]
00:00:40>> Раз уж мы все признали, что поставляем
00:00:41код, который больше не понимаем, я хочу совершить небольшое путешествие,
00:00:44чтобы понять, как мы к этому пришли.
00:00:46Во-первых, заглянем в историю — она имеет свойство повторяться.
00:00:50Во-вторых, мы попали в своего рода ловушку.
00:00:52Мы перепутали понятия «легко» и «просто».
00:00:55И наконец, решение есть, но оно требует от нас не перекладывать мышление на других.
00:01:00Последние несколько лет я работал в Netflix, внедряя инструменты ИИ.
00:01:05И должен сказать, что ускорение процессов абсолютно реально.
00:01:07Задачи из бэклога, которые раньше занимали дни, теперь решаются за часы.
00:01:10А крупные рефакторинги, которые висели годами, наконец-то сдвинулись с мертвой точки.
00:01:15Но есть один нюанс.
00:01:16Большие продакшн-системы всегда выходят из строя неожиданным образом.
00:01:19Посмотрите, например, на недавний случай с Cloudflare.
00:01:21И когда такое случается, лучше бы вам понимать код, который вы отлаживаете.
00:01:23Проблема в том, что сейчас мы генерируем код с такой скоростью и в таком объеме,
00:01:28что наше понимание за ним просто не поспевает.
00:01:29Черт возьми, я и сам через это проходил.
00:01:34Нагенерировал кучу кода, посмотрел на него и подумал: «Понятия не имею, что это делает».
00:01:39Но тесты прошли, все работало, и я его задеплоил.
00:01:41Важно понимать, что в этом нет ничего нового.
00:01:44Каждое поколение инженеров рано или поздно упиралось в стену,
00:01:48когда сложность ПО превышала их способности по управлению им.
00:01:50Мы не первые, кто столкнулся с кризисом программного обеспечения,
00:01:52но мы первые, кто столкнулся с ним в масштабах бесконечной генерации.
00:01:56Давайте вернемся назад и посмотрим, с чего все началось.
00:01:58В конце 60-х — начале 70-х группа выдающихся ученых
00:02:03собралась и заявила: «Эй, у нас кризис ПО».
00:02:06Спрос на программы огромен, но мы не успеваем его удовлетворять.
00:02:11Проекты затягиваются, все движется слишком медленно.
00:02:15Мы справляемся плохо.
00:02:16И тогда Дейкстра выдал отличную цитату.
00:02:20Он сказал (перефразирую): когда у нас было
00:02:23несколько слабых компьютеров, программирование было скромной проблемой.
00:02:26А теперь, когда у нас гигантские компьютеры, программирование стало гигантской проблемой.
00:02:31Он объяснял, что по мере того, как мощность «железа» росла в тысячи раз,
00:02:34потребности общества в софте росли пропорционально.
00:02:37И нам, программистам, пришлось искать баланс между целями и средствами,
00:02:41как поддерживать такие объемы ПО?
00:02:43Это происходит циклично.
00:02:47В 70-х появился язык Си, чтобы мы могли писать более крупные системы.
00:02:50В 80-х пришли персональные компьютеры — теперь каждый мог писать код.
00:02:53В 90-х появилось объектно-ориентированное программирование.
00:02:56Иерархии наследования из ада — «спасибо» Java за это.
00:03:00В 2000-х пришел Agile со спринтами и
00:03:03скрам-мастерами, которые говорили, что делать; «водопад» ушел в прошлое.
00:03:06В 2010-х — облака, мобилки, DevOps... софт
00:03:09по-настоящему поглотил мир.
00:03:10И вот сегодня у нас есть ИИ: Co-Pilot, Cursor, Claude, Codex, Gemini и прочие.
00:03:17Мы можем генерировать код так же быстро, как описываем его.
00:03:19Закономерность та же, но масштаб изменился — теперь он бесконечен.
00:03:23Фред Брукс, известный по книге «Мифический человеко-месяц»,
00:03:29в 1986 году написал статью под названием «Серебряной пули нет».
00:03:32В ней он утверждал, что не появится ни одной инновации,
00:03:36которая даст десятикратный скачок в производительности труда программистов.
00:03:38Почему?
00:03:40Потому что, по его словам, сложность не в механике кодинга: не в синтаксисе,
00:03:44не в наборе текста и не в шаблонном коде.
00:03:45Сложность — в понимании самой проблемы и проектировании решения.
00:03:49Ни один инструмент не может устранить эту фундаментальную трудность.
00:03:52Все инструменты и техники до настоящего момента облегчали лишь механику.
00:03:55Но основная задача —
00:03:57понять, что именно строить и как это должно работать — остается такой же трудной.
00:04:00Если проблема не в механике, почему мы продолжаем её оптимизировать?
00:04:06Как опытные инженеры приходят к коду, который сами не понимают?
00:04:09Ответ, я думаю, кроется в двух словах, которые мы часто путаем: «просто» и «легко».
00:04:14Мы привыкли использовать их как синонимы,
00:04:16но на самом деле они означают совершенно разные вещи.
00:04:18На ужине спикеров меня «разоблачили» как фаната Clojure, так что
00:04:21здесь все станет ясно.
00:04:23Рич Хикки, создатель языка Clojure,
00:04:25объяснил это в своем докладе 2011 года «Simple Made Easy».
00:04:29Он определил «простое» (simple) как нечто неспутанное, состоящее из одного слоя или нити.
00:04:33Каждая часть делает одну вещь и не переплетается с другими.
00:04:36«Легкое» (easy) он определил как нечто лежащее рядом, то, что под рукой.
00:04:39То, к чему можно получить доступ без усилий.
00:04:41Скопировал, вставил, отправил.
00:04:43«Просто» — это про структуру.
00:04:45«Легко» — это про доступность.
00:04:48Проблема в том, что нельзя сделать вещь «простой» по щучьему велению.
00:04:51Простота требует размышлений, проектирования и распутывания узлов.
00:04:54Но сделать что-то «легче» можно всегда.
00:04:56Нужно просто положить это поближе.
00:04:57Установить пакет, сгенерировать через ИИ, скопировать решение со Stack Overflow.
00:05:03Идти легким путем — в человеческой природе.
00:05:06Мы так запрограммированы.
00:05:07Как я уже сказал, скопировать со Stack Overflow — это же прямо здесь.
00:05:10Фреймворк, который делает все за тебя с помощью «магии»: установил и погнал.
00:05:14Но «легко» не значит «просто».
00:05:15«Легко» означает, что вы можете быстро что-то добавить в систему.
00:05:18«Просто» означает, что вы способны понять сделанную работу.
00:05:20Выбирая «легко», мы выбираем скорость сейчас ценой сложности потом.
00:05:24Честно говоря, раньше этот компромисс работал.
00:05:27Сложность в кодовой базе накапливалась достаточно медленно,
00:05:31что позволяло нам рефакторить, переосмыслять и перестраивать всё при необходимости.
00:05:34Я считаю, что ИИ нарушил этот баланс.
00:05:36Потому что это идеальная «кнопка легко».
00:05:37Он делает легкий путь настолько
00:05:38беспрепятственным, что мы даже не рассматриваем простой путь.
00:05:41Зачем думать об архитектуре, когда код появляется мгновенно?
00:05:44Позвольте мне показать, как это происходит.
00:05:47Как простая задача превращается в месиво сложности
00:05:50через диалоговый интерфейс, который мы все так полюбили.
00:05:52Это надуманный пример, но допустим, у нас есть приложение,
00:05:55и мы хотим добавить в него аутентификацию.
00:05:57Пишем «добавь auth», и получаем чистенький файл auth.js.
00:06:01После пары итераций это сообщение номер пять. Окей, круто.
00:06:02Дальше: «Давай добавим OAuth», и
00:06:04теперь у нас есть auth.js и OAuth.js.
00:06:07Мы продолжаем, и вдруг обнаруживаем, что сессии сломались.
00:06:11Появилась куча конфликтов.
00:06:12К 20-му шагу это уже не обсуждение.
00:06:15Вы управляете контекстом, который стал настолько сложным, что вы сами
00:06:18не помните всех ограничений, которые в него добавили.
00:06:20Мертвый код от заброшенных подходов.
00:06:22Тесты, которые «починили», просто заставив их как-то работать.
00:06:25Фрагменты трех разных решений, потому что в процессе вы говорили: «Ой, подожди».
00:06:28Каждая новая инструкция перезаписывает архитектурные паттерны.
00:06:31Мы сказали «сделай, чтобы авторизация работала» — она заработала.
00:06:33Сказали «исправь эту ошибку» — она исправила.
00:06:35Плохим архитектурным решениям нет никакого сопротивления.
00:06:38Код просто мутирует, чтобы удовлетворить ваш последний запрос.
00:06:40Каждое взаимодействие — это выбор «легкого» вместо «простого».
00:06:43А «легко» всегда означает рост сложности.
00:06:46Мы знаем, как лучше, но когда легкий путь настолько легок, мы выбираем его.
00:06:50И сложность будет накапливаться, пока не станет слишком поздно.
00:06:52ИИ по-настоящему доводит идею «легкости» до логического предела.
00:06:58Решаете, что хотите — и мгновенно получаете код.
00:07:00Но в этом таится опасность.
00:07:02Сгенерированный код относится ко всем паттернам в вашей базе одинаково.
00:07:06Когда агент анализирует ваш код, каждая строка становится образцом для подражания.
00:07:10Проверка авторизации в строке 47? Это паттерн.
00:07:13Тот странный gRPC-код, косящий под GraphQL, который я добавил в 2019-м?
00:07:18Это тоже паттерн.
00:07:19Технический долг не распознается как долг — это просто еще одна порция кода.
00:07:22Настоящая проблема здесь — сложность.
00:07:25Я уже много раз произнес это слово в докладе, не дав ему четкого определения.
00:07:29Лучше всего думать об этом как о противоположности простоте.
00:07:31Это означает «переплетенность».
00:07:33Когда вещи сложны, всё соприкасается со всем остальным.
00:07:36Вы не можете изменить одну деталь, не затронув десяток других.
00:07:41Вернемся к статье Фреда Брукса «Серебряной пули нет».
00:07:43В ней он выделил два основных типа сложности в любой системе.
00:07:47Есть существенная (essential) сложность — это фундаментальная трудность
00:07:51самой задачи, которую вы пытаетесь решить.
00:07:53Пользователям нужно платить, заказы должны выполняться.
00:07:56Это сложность того, ради чего ваша программа вообще существует.
00:08:00И есть второй тип — случайная (accidental) сложность.
00:08:03Все то, что мы добавили по ходу дела: «костыли», защитный код,
00:08:06фреймворки, абстракции, которые когда-то казались разумными.
00:08:09Это всё, что мы наворотили, чтобы заставить сам код работать.
00:08:11В реальном коде эти два типа сложности повсюду.
00:08:16Они так переплетены, что их разделение требует контекста,
00:08:19знания истории и опыта.
00:08:20Результат работы ИИ не делает таких различий.
00:08:24И поэтому каждый кривой паттерн продолжает копироваться.
00:08:26Вот реальный пример из нашей работы в Netflix.
00:08:32У меня есть система со слоем абстракции между нашим старым
00:08:35кодом авторизации пятилетней давности и новой централизованной системой.
00:08:41У нас не было времени переписывать всё приложение целиком,
00:08:42поэтому мы просто вставили «прослойку» (shim).
00:08:44И вот появился ИИ — отличный шанс отрефакторить код,
00:08:47чтобы использовать новую систему напрямую. Кажется, простая задача, да?
00:08:50Нет. Старый код был слишком плотно привязан
00:08:56к своим паттернам авторизации: проверки прав были вплетены в бизнес-логику,
00:08:59предположения о ролях зашиты в модели данных, а вызовы разбросаны по сотням файлов.
00:09:03Агент начинал рефакторинг, проходил несколько файлов,
00:09:07упирался в зависимость, которую не мог распутать, уходил в штопор и сдавался.
00:09:10Или, что еще хуже, он пытался сохранить логику старой системы,
00:09:16воспроизводя её через новую, что тоже никуда не годится.
00:09:19Беда в том, что он не видел границ.
00:09:23Он не мог понять, где заканчивается бизнес-логика и начинается логика авторизации.
00:09:26Все было настолько запутано, что даже с полной информацией
00:09:30ИИ не мог найти чистого пути.
00:09:33Когда ваша «случайная сложность» так сильно переплетена,
00:09:35ИИ — не лучший помощник в том, чтобы сделать код лучше.
00:09:38Я обнаружил, что он только добавляет новые слои сверху.
00:09:40Мы-то можем отличить одно от другого — по крайней мере, когда замедляемся, чтобы подумать.
00:09:45Мы знаем, какие паттерны важны,
00:09:47а какие — просто чье-то решение многолетней давности.
00:09:50Мы обладаем контекстом, который ИИ не может вывести логически,
00:09:53но только если мы потратим время на эти разграничения перед началом работы.
00:09:56Так как же это сделать на практике?
00:10:01Как отделить случайную сложность от существенной,
00:10:04когда перед тобой огромная кодовая база?
00:10:07В той базе Netflix, где я работаю, около миллиона строк на Java,
00:10:10а основной сервис в ней занимает около пяти миллионов токенов, по последним замерам.
00:10:13Ни одно контекстное окно, к которому у меня есть доступ, не вместит это целиком.
00:10:17Сначала я подумал: эй,
00:10:19может, просто скопировать огромные куски кода в контекст
00:10:23и посмотреть, не проявятся ли паттерны сами собой,
00:10:24не поймет ли система, что происходит.
00:10:26Но, как и с рефакторингом авторизации, результат просто утонул в собственной сложности.
00:10:29результат просто утонул в собственной сложности.
00:10:31В этой ситуации мне пришлось попробовать что-то другое.
00:10:34Нужно было отобрать самое важное: документы по дизайну, архитектуру, диаграммы,
00:10:37ключевые интерфейсы и всё в таком духе.
00:10:39И потратить время на описание требований к взаимодействию компонентов
00:10:42и того, каким паттернам нужно следовать.
00:10:43По сути, я писал спецификацию.
00:10:45Пять миллионов токенов превратились в 2000 слов спецификации.
00:10:49А затем, пойдя еще дальше, я взял эту спецификацию
00:10:52и составил точный набор шагов для выполнения в коде.
00:10:55Никаких расплывчатых инструкций, только четкая последовательность операций.
00:10:58Я обнаружил, что это дает гораздо более чистый и сфокусированный код, который я могу понять.
00:11:02То есть я сначала всё определил и спланировал само выполнение.
00:11:05Этот подход я какое-то время назад назвал «компрессией контекста».
00:11:11Но вы можете называть это контекстной инженерией, спектральной разработкой —
00:11:13как вам угодно.
00:11:15Название не имеет значения.
00:11:16Важно лишь то, что обдумывание и планирование становятся основной частью работы.
00:11:20Позвольте мне показать, как это работает на практике.
00:11:22Итак, этап первый, фаза один: исследование.
00:11:26Я скармливаю ИИ всё необходимое заранее.
00:11:28Схемы архитектуры, документацию, ветки обсуждений в Slack.
00:11:31Мы об этом уже много раз говорили.
00:11:32Но действительно — соберите как можно больше контекста, имеющего отношение
00:11:35к изменениям, которые вы вносите.
00:11:36А затем используйте агента, чтобы проанализировать кодовую базу,
00:11:39составить карту компонентов и зависимостей.
00:11:42Это не должно быть разовым действием.
00:11:43Мне нравится «прощупывать» систему, спрашивая: «А что насчет кэширования?»
00:11:46«Как здесь обрабатываются сбои?»
00:11:47И когда анализ ИИ неверен, я его поправляю.
00:11:49Если ему не хватает контекста — я его предоставляю.
00:11:51Каждая итерация уточняет этот анализ.
00:11:55Результатом здесь является единый исследовательский документ.
00:11:57Вот что уже есть, вот как оно связано, и
00:11:59вот на что повлияют ваши изменения.
00:12:01Часы изучения сжимаются в минуты чтения.
00:12:03Декс упоминал это утром, но человеческая проверка здесь критически важна.
00:12:09Именно в этот момент вы сверяете анализ с реальностью,
00:12:12и это самый эффективный момент во всем процессе.
00:12:15Найдите ошибки здесь — предотвратите катастрофу позже.
00:12:17Переходим ко второй фазе.
00:12:20Теперь, когда у вас на руках проверенные данные исследования,
00:12:22мы создаем детальный план реализации: реальную структуру кода,
00:12:25сигнатуры функций, определения типов, потоки данных.
00:12:28Нужно, чтобы любой разработчик мог этому следовать.
00:12:30Я сравниваю это с раскраской по номерам.
00:12:32Вы должны иметь возможность отдать это самому младшему инженеру со словами: «Делай».
00:12:35И если он перепишет это строка за строкой, всё должно просто заработать.
00:12:38На этом этапе мы принимаем множество важных архитектурных решений.
00:12:43Убеждаемся, что сложная логика верна.
00:12:45Проверяем, соответствуют ли бизнес-требования лучшим практикам.
00:12:50Следим за границами сервисов, чистотой разделения и
00:12:52предотвращаем любое излишнее зацепление (coupling).
00:12:54Мы замечаем проблемы заранее, потому что сами через это проходили.
00:12:57У ИИ такой возможности нет.
00:12:59Он воспринимает любой шаблон как обязательное требование.
00:13:01Настоящая магия этого шага — в скорости проверки.
00:13:05Мы можем утвердить этот план за минуты и точно знать, что будет построено.
00:13:10И чтобы успевать за скоростью генерации кода,
00:13:13мы должны уметь осмыслить свои действия так же быстро.
00:13:18Наконец, фаза реализации. Теперь, когда у нас есть четкий план,
00:13:22подкрепленный исследованием, этот этап должен быть довольно простым.
00:13:26В этом-то и суть.
00:13:28Когда у ИИ есть четкая спецификация, контекст остается чистым и
00:13:31сфокусированным.
00:13:32Мы предотвратили спираль сложности длинных диалогов.
00:13:36Вместо 50 сообщений с постоянно меняющимся кодом,
00:13:38у нас есть три четких результата, каждый из которых проверен перед следующим шагом.
00:13:41Никаких брошенных подходов, конфликтующих паттернов
00:13:44или моментов типа «ой, подождите», оставляющих повсюду мертвый код.
00:13:48Для меня главный плюс в том, что можно поручить большую часть этой работы фоновому агенту,
00:13:52потому что вы уже проделали всю мыслительную и сложную работу заранее.
00:13:56Он может просто начать внедрение, пока вы занимаетесь чем-то другим,
00:13:59а затем вернуться для проверки.
00:14:01И вы сможете проверить это быстро, потому что вам нужно лишь убедиться,
00:14:04что всё идет по плану, а не пытаться понять, не нафантазировал ли ИИ чего лишнего.
00:14:07Суть в том, что мы не используем ИИ, чтобы он думал за нас.
00:14:12Мы используем его для ускорения механической части,
00:14:15сохраняя при этом способность понимать происходящее.
00:14:17Исследование идет быстрее, планирование становится глубже, а реализация — чище.
00:14:21Но мышление, синтез и суждения — это остается за нами.
00:14:26Помните тот рефакторинг авторизации, с которым ИИ не справлялся?
00:14:34Дело в том, что сейчас мы над ним работаем
00:14:37 и уже достигли хорошего прогресса.
00:14:39И причина не в том, что мы подобрали промпты получше.
00:14:42Мы поняли, что не можем сразу перепрыгнуть к исследованию, планированию
00:14:45и реализации.
00:14:46Нам пришлось внести это изменение вручную.
00:14:49Без ИИ — просто читая код, вникая в зависимости
00:14:52и меняя что-то, чтобы посмотреть, где «отвалится».
00:14:53Честно скажу, эта ручная миграция была мучительной, но крайне важной.
00:14:59Она выявила все скрытые ограничения, инварианты, которые должны соблюдаться,
00:15:02и сервисы, которые сломаются при изменении авторизации.
00:15:05Вещи, которые никакой статический анализ кода бы нам не выдал.
00:15:09Затем мы добавили этот пулл-реквест с ручной миграцией в наш процесс исследования,
00:15:14использовав его как основу для всей дальнейшей работы.
00:15:19Тогда ИИ смог увидеть, как выглядит «чистая» миграция.
00:15:23Все сущности немного отличаются, поэтому нам всё равно приходится спрашивать ИИ:
00:15:27«А что мы будем делать в этом случае?»
00:15:29Где-то данные зашифрованы, где-то нет.
00:15:32Нам приходилось каждый раз давать дополнительный контекст через итерации.
00:15:35И только тогда мы смогли создать план, который мог сработать с первого раза.
00:15:41И «мог» здесь ключевое слово, потому что мы всё еще проверяем,
00:15:45подстраиваем и обнаруживаем пограничные случаи.
00:15:47Трехфазный подход — это не магия.
00:15:55Он работает только потому, что мы сделали одну миграцию вручную.
00:15:57Нам пришлось заслужить это понимание, прежде чем встроить его в процесс.
00:16:01Я по-прежнему считаю, что «серебряной пули» не существует.
00:16:02Никакие промпты, модели или даже крутые спецификации её не заменят.
00:16:06Только глубокое понимание своей системы позволяет
00:16:09вносить в неё изменения безопасно.
00:16:11Так зачем вообще всё это нужно?
00:16:15Почему бы просто не перебирать варианты с ИИ, пока не заработает?
00:16:18Рано или поздно модели станут мощнее, и всё будет работать само.
00:16:21Но, на мой взгляд, «просто работает» — этого недостаточно.
00:16:24Есть разница между кодом, который проходит тесты, и кодом, который выживет в продакшене.
00:16:28Между системами, которые функционируют сегодня,
00:16:31и системами, которые кто-то другой сможет изменить в будущем.
00:16:34Настоящая проблема — это дефицит знаний.
00:16:38Когда ИИ может генерировать тысячи строк кода за секунды,
00:16:41его понимание может занять у вас часы, а если код сложный — то и дни.
00:16:45А может, вы и вовсе никогда его не поймете, если он слишком запутанный.
00:16:48Вот о чем, как мне кажется, сейчас мало кто говорит.
00:16:52Каждый раз, когда мы перестаем думать, чтобы поспеть за скоростью генерации,
00:16:56мы не просто добавляем код, который не понимаем.
00:16:58Мы теряем способность распознавать проблемы.
00:17:00Инстинкт, который говорит: «Эй, тут становится слишком сложно»,
00:17:03атрофируется, когда вы не понимаете собственную систему.
00:17:09Распознавание паттернов приходит с опытом.
00:17:11Когда я вижу опасную архитектуру,
00:17:12это потому, что я сам в три часа ночи разгребал последствия её сбоев.
00:17:16Когда я настаиваю на более простых решениях,
00:17:17это потому, что мне приходилось поддерживать чужие переусложненные альтернативы.
00:17:21ИИ генерирует то, что вы просите.
00:17:23Он не учитывает уроки прошлых ошибок.
00:17:25Трехфазный подход помогает преодолеть этот разрыв.
00:17:29Он упаковывает понимание в артефакты, которые мы можем проверять со скоростью генерации.
00:17:33Без этого мы просто накапливаем сложность быстрее,
00:17:37чем успеваем её осознать.
00:17:39ИИ меняет всё в том, как мы пишем код, но, честно говоря,
00:17:44я не думаю, что он меняет причины, по которым софт ломается.
00:17:47Каждое поколение сталкивалось со своим кризисом программного обеспечения.
00:17:50Поколение Дейкстры решало его созданием дисциплины программной инженерии,
00:17:54а мы сталкиваемся со своим кризисом — бесконечной генерацией кода.
00:17:56Я не думаю, что решение кроется в очередном инструменте или методологии.
00:18:01Важно помнить то, что мы знали всегда: разработка ПО — это человеческое дело.
00:18:05Самым сложным никогда не было набрать код на клавиатуре.
00:18:06Сложно было знать, что именно нужно написать.
00:18:09Преуспеют не те разработчики, которые генерируют больше всего кода.
00:18:13А те, кто понимает, что именно они строят,
00:18:15кто видит стыки и может вовремя осознать,
00:18:18что они решают не ту проблему.
00:18:19Это всё еще наша роль.
00:18:20И это всегда будет только нашей ролью.
00:18:21В завершение я хочу оставить вам вопрос. И он не в том,
00:18:25будем ли мы использовать ИИ.
00:18:26Это решенный вопрос.
00:18:28Этот корабль уже уплыл.
00:18:30Для меня вопрос в том, будем ли мы по-прежнему понимать собственные системы,
00:18:33когда ИИ будет писать большую часть нашего кода.
00:18:35Спасибо.
00:18:37>> [АПЛОДИСМЕНТЫ]
00:18:39[МУЗЫКА]