Skip to content

Latest commit

 

History

History
195 lines (145 loc) · 11.9 KB

File metadata and controls

195 lines (145 loc) · 11.9 KB

Второй пилот — не капитан

В начале книги уже было отмечено: код читают гораздо чаще, чем пишут. Сегодня у разработчика появился второй пилот — только теперь он не сидит рядом, а встроен прямо в редактор. ChatGPT, Copilot и другие LLM-инструменты. Быстрые, дружелюбные, полезные. Но не стоит обманываться: это всё ещё второй пилот, а не капитан.

Обычно код не меняют просто так. Чаще всего причина — баг или новая функциональность, и задача для инструмента формулируется примерно так:

«Выступи в роли X. Иногда этот код выбрасывает исключение XXX. Исправь, пожалуйста».

Но именно в таких формулировках и кроется проблема.

Если передать ему метод на 800 строк, полных противоречий, логических дыр и нелепых зависимостей — не надейтесь, что он сделает из этого конфетку. Он будет продолжать. Механически, без разбора. Потому что его задача — продолжить твой стиль, а не исправить твой хаос. Старое правило GIGO — *Garbage In, Garbage Out*. никуда не делось.

Рассмотрим простой пример: функция, которая записывает email в базу и отправляет приветственное письмо. Попросим второго пилота добавить логирование:

// >_ Добавь логирование адресов почты

function store()
{
    $email = $_POST['email'];

    $conn = new mysqli('localhost', 'user', 'pass', 'db');
    $conn->query("INSERT INTO users (email) VALUES ('$email')");

    mail($email, 'Hi', 'Welcome!');

+    file_put_contents(
+        'log.txt',
+        "User $email\n",
+        FILE_APPEND
+    );

    echo 'Done';
}

Он чертовски хорошо выполнил свою задачу, но проблема не в его работе, а в том, что переданный код выглядит очень плохо, потому что в нём:

  • Нет возврата осмысленного результата.
  • SQL-инъекция (строка вставляется напрямую).
  • Нет обработки ошибок.

Если разработчик передаёт мусор — метод, который и швец, и жнец, и на дуде игрец — не стоит удивляться, что в ответ получит ещё и трубача.
LLM не откажет. Он скажет: «Да, командир!» — и продолжит работу.

На сайтах вроде Stack Overflow вопросы задают и решают живые люди. Часто можно встретить ответы, отмеченные как «решение», но при этом получившие минусы — в комментариях объясняют: да, это работает, но в долгосрочной перспективе приведёт к серьёзным проблемам.

"Второй пилот" — помощник другого типа.

Он не будет указывать на архитектурные проблемы или спорные решения, пока его об этом не попросите. Его задача — помогать вам как можно быстрее двигаться к результату, решать бизнес-проблемы здесь и сейчас. Но и большинство разработчиков настроены точно так же: задача должна быть закрыта, сроки — вчера, и мало кто будет тратить время, чтобы просить помощника подумать над более выразительными именами переменных, архитектурой или стилем.

Поэтому пилот продолжает ехать по плохой дороге. А это самое страшное — мы просто раздуваем хаос. И не просто так, а ещё и автоматизируем его рост.

Вот почему важно начать с хорошей базы: чистого кода, понятной архитектуры, простых методов и имён.

Контекст

У LLM-инструментов есть ограничение, про которое редко говорят — окно контекста. Оно невидимо, его нельзя контролировать напрямую. Но оно всегда рядом. И иногда критически важные части твоей системы просто выпадают из внимания модели.

Появляется асимметрия. Поверхность кажется правильной, но под капотом — несовместимость.

Мы уже несколько раз в книге говорили о симметрии, но давайте повторим. И посмотрим на небольшой пример импортера исторических данных о погоде:

$importer = new WeatherHistoryImporter();
$importer->import([
    'devices'   => '/tmp/weather/devices.xml',
    'locations' => '/tmp/weather/locations.xml',
    'readings'  => '/tmp/weather/readings.xml',
]);

Его мог бы написать любой, и выглядит вполне разумно. Но этот класс — часть процесса импорта/экспорта. LLM может не знать о том, что класс экспорта выдаёт не набор XML-структур, а zip-архив:

$exporter = new WeatherHistoryExporter();
$exporter->export('/tmp/weather.zip');

И мы снова получим асимметрию, хотя между экспортом и импортом должна быть полная совместимость. Результат экспорта по идее должен сразу и без искажений становиться аргументом для импорта — чтобы данные шли по замкнутому кругу без потерь и несоответствий.

И что? Они просто не стыкуются. LLM не «ошибся». Он просто не увидел целиком весь процесс.

Вот почему важно проверять границы. Убедиться, что один этап действительно продолжает другой. Что данные не просто «появились», а дошли по цепочке. Что код — не набор изолированных кусочков, а единое движение данных.

Именно контекст определяет смысл. И если ты его теряешь — модель его точно не найдёт.

Другой пример такой асимметрии — если мы попросим LLM, например, внести проверку на уникальность в базе данных:

class TokenService
{
    public function generate(): string
    {
        return bin2hex(random_bytes(16));
    }
}

В ответ получишь:

public function generate(): string
{
    do {
        $token = bin2hex(random_bytes(16));
    } while ($this->canTokenExists($token));

    return $token;
}

private function canTokenExists(string $token): bool
{
    return DB::table('tokens')
        ->where('value', $token)
        ->exists();
}

Он так же отлично справился с задачей. Но вот что он не знает: в другом месте системы у тебя уже есть TokenRepository, и именно он отвечает за сохранение, валидацию, поиск, всё, что связано с токенами. И теперь у тебя два источника правды: один в TokenService, второй в TokenRepository.

Почему это произошло? Всё по той же причине — потому что пилот не видит всей картины. Он сфокусирован на том куске, который ему показали. И всё остальное — выпадает.

Контекст — это не «удобно держать в голове». Это то, без чего невозможно принимать архитектурные решения. LLM не знает, как устроена твоя система. Он просто не видит границ. А разработчик — видит. Или, по крайней мере, должен видеть.

Не твой код

Есть и ещё одна неочевидная причина, по которой работа второго пилота может вызывать раздражение или разочарование, даже если он выполнил задачу точно.

Для очень многих разработчиков код — это не просто способ что-то реализовать. Это форма мышления, контроля, развития. Это способ выразить себя через структуру, стиль, архитектурные решения.

Когда множество программного кода рождается не в процессе размышлений, а просто появляется по запросу — остаётся ощущение отчуждённости. Да, задача решена. Но путь к решению пройден не тобой. Ты не выбирал между подходами, не ошибался, не искал компромисс. А значит — не чувствуется и результат.

Сгенерированный код может выглядеть и работать правильно, но разработчик становится не уверен в его деталях, не понимает всех нюансов и не чувствует связи с ним. Он становится чем-то внешним — как инструмент, который решил задачу, но не передал опыт.

В результате со временем может очень сильно снизиться мотивация и вовлечённость разработчика. Чтобы такого не допускать, нужно не просто «принимать» результат от LLM, а делать его "своим".

  • Понимать, откуда он взялся.
  • Переписывать под собственный стиль.
  • Встраивать в архитектуру осознанно.

AI — это помощник. Он может ускорить работу. Но он не заменит твой выбор, твой стиль, твоё мышление.

Второй пилот не заменит твоё мышление — он просто помогает писать. А хороший код начинается с тебя!