Всем привет, меня зовут Михаил, я работаю senior Java/Kotlin разработчиком в ОТП Банке. В свободное от работы время я занимаюсь менторством. Ко мне приходят разные люди: кто-то уже работает на проекте, кто-то только учится.

Сейчас, в эпоху ИИ, найти информацию стало куда легче, чем раньше. Раньше ты гуглил и тыкал по ссылкам, а теперь все в одном приложении. Да еще и код можно не писать - ну красота же. Но что, если ты и до этого не умел писать код и решил "вкатываться в айтишку" (ненавижу эту фразу)?

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

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

Сегодня поговорим о том, как ИИ влияет на обучение с нуля и как им лучше пользоваться для обучения.

!!!ДИСКЛЕЙМЕР!!!

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

Суть проблемы

Раньше, когда информация не лежала в одном месте, а код нейросеть не писала под твой проект, приходилось делать все руками. Ты гуглил, открывал 10 вкладок, читал статьи на Stack Overflow, смотрел примеры и думал: "А как это применить к моей задаче?" Иногда часами сидел над одним методом. Писал, переписывал, падал, вставал и снова писал.

Это было больно. Но это набивало опыт.

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

Сейчас все по-другому.

Перед разработчиком стоит искушение: скормить задачу нейросети и через 30 секунд получить готовый код. Красота же. И многие поддаются. Особенно те, кто только учится. Зачем думать, если можно просто скопировать? Зачем ошибаться, если нейросеть выдаст правильный вариант?

И люди обленились. Честно. Они перестали набивать шишки. Они перестали думать. Они разучились читать чужой код, потому что нейросеть написала свой. Они не знают, почему их код работает - просто потому что "нейросеть сказала, что так правильно".

Проблема даже не в том, что мидлы или сеньоры делают все с нейросетями. Нет. Проблема именно в начинающих специалистах. И весь парадокс в том, что они боятся, что их полностью заменят нейросети. При этом пользуются только ими и доверяют им все. Сами себя заменяют.

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

Именно об этом моя статья.

Задача и подопытные

Задача достаточно простая:

Задача
class NotificationRequest {
    private String requestId;
    private NotificationType type;
    private String recipient;
    private String message;
}
 
enum NotificationType {
    EMAIL,
    SMS,
    PUSH
}

Реализовать программу, которая:

1. Принимает список запросов и обрабатывает данные по одному

2. Не отправляет дубликаты

Дубликатом считается запись с одинаковым requestId.То есть если запрос уже обрабатывался - повторно не отправляем.

3.Регистрация считается невалидной если:

requestId == null
recipient == null
message == null

Email невалидный если:
не содержит "@"

SMS невалидный если :
номер телефона не содержит "+" и не содержит ровно 11 символов после плюса.

PUSH невалидный если:
Сообщение пустое

4. Эмулирует отправку(просто вывод в лог информации)
Создать сервис:

NotificationService

Метод:

void send(NotificationRequest request)

Sending EMAIL to user1@mail.com
Sending SMS to +79990001122
Sending PUSH to device-123

После обработки вывести:

Processed: 3
Duplicates skipped: 2
Failed: 0

ВАЖНО! Используем только Java, ничего больше!

В качестве подопытных будут следующие лица:

Кто

Пользование нейросетью

Нейросеть (DeepSeek и ChatGPT, их самые доступные версии)

Да

Стажер

Да

Стажер

Нет

Мидл, который больше не пишет код сам (вайбкодер)

Да

Сеньор

Как захочет (почему так, узнаем позже)

Для нейросети я взял самые доступные - deepseek и chatgpt их самые доступные версии.

Нейросеть

Я сказал просто: «Реши задачу». Без сложного промта, получил следующее решение -

Решение нейросети
class NotificationService {
    private Set<String> processedIds = new HashSet<>();
    private int processedCount = 0;
    private int duplicatesSkipped = 0;
    private int failedCount = 0;

    public void send(NotificationRequest request) {
        if (processedIds.contains(request.getRequestId())) {
            duplicatesSkipped++;
            return;
        }

        if (!isValidRegistration(request)) {
            failedCount++;
            return;
        }

        if (!isValidByType(request)) {
            failedCount++;
            return;
        }

        emulateSending(request);
        processedIds.add(request.getRequestId());
        processedCount++;
    }

    private boolean isValidRegistration(NotificationRequest request) {
        return request.getRequestId() != null 
            && request.getRecipient() != null 
            && request.getMessage() != null;
    }

    private boolean isValidByType(NotificationRequest request) {
        switch (request.getType()) {
            case EMAIL:
                return request.getRecipient().contains("@");
            case SMS:
                String phone = request.getRecipient();
                return phone.startsWith("+") && phone.length() == 12;
            case PUSH:
                return !request.getMessage().trim().isEmpty();
            default:
                return false;
        }
    }

    private void emulateSending(NotificationRequest request) {
        switch (request.getType()) {
            case EMAIL:
                System.out.println("Sending EMAIL to " + request.getRecipient());
                break;
            case SMS:
                System.out.println("Sending SMS to " + request.getRecipient());
                break;
            case PUSH:
                System.out.println("Sending PUSH to " + request.getRecipient());
                break;
        }
    }

    public void printStatistics() {
        System.out.println("\nProcessed: " + processedCount);
        System.out.println("Duplicates skipped: " + duplicatesSkipped);
        System.out.println("Failed: " + failedCount);
    }
}

Видим следующие решение - оно в лоб, ничего сложного, нет абстракций, паттернов или чего-то еще Видим следующие решение - оно в лоб, ничего сложного, нет абстракций, паттернов или чего-то еще

Оно в лоб. Ничего сложного. Нет абстракций, паттернов или чего-то еще. Оно работает, но нет гибкости. Нет архитектуры. Просто код, который делает то, что просили, и не более.

Дальше я не буду вставлять код каждого решения. Считаем, что этот код - основа, которую мы будем совершенствовать.

Стажер, который решал задачу с чатом.

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

Стажер, который решает задачу сам.

Здесь для меня было удивление. Решение заняло куда больше времени, чем я ожидал. У стажера без ИИ ушло в районе 2-3 часов на решение.

И знаете что? Я горжусь его решением.

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

  • Фабрика - для создания объектов NotificationService.

  • Синглтон - для создания одного экземпляра на каждый тип.

  • Фабричный метод - так как логика везде одинаковая, была сделана абстракция с одним абстрактным методом validate.

Возможно, это решение не считается идеальным. Да и нет идеальных решений. Но оно гораздо лучше. Гораздо гибче. В нем видна идея.

Человек, который подумал своей головой, поискал решения - получил хороший результат.

Да мы получили решение куда дольше, но зато его можно легко поддерживать!

Мидл - вайбкодер

Как он сюда попал? На самом деле совершенно случайно, это человек, который уже долго работает на реальном проекте и сейчас почти полностью пишет код с нейросетью. Мне показалось, что это идеальный кандидат. Впервые за долгое время он решал задачу сам и вот итог:

Есть NotificationService, который занимается отправкой и проверкой дубликатов. Есть отдельный ValidationService, который содержит методы smsValidation, pushValidation, emailValidation.

Оно лучше, чем у нейросети. Но не идеальное.

Что хорошо: есть разделение ответственности. NotificationService занимается отправкой, ValidationService - валидацией. Не все в одной куче. Есть разделение на подметоды в основном коде.

Что плохо: нет гибкости. Чтобы добавить новый тип уведомления (например Telegram), придется лезть в класс ValidationService и добавлять новый метод. И в NotificationService тоже. Нет абстракции, нет паттернов, нет расширяемости.

Это среднее решение. Оно работает. Но развивать его страшно.

Хотя при использовании ИИ и просмотра его кода качество было всегда куда лучше.

Сеньор

Почему я написал в таблице загадку - "Как захочет (почему так, узнаем позже)"?

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

Итоговое решение получилось коротким, четким. Использовались абстракции, паттерны. Предусмотрено логирование для каждого сценария. Вынесены константы.

На все про все у сеньора ушло 30-40 минут. Из них непосредственно ревью кода нейросети с правками заняло около 10 минут. Понадобилось 3 итерации правок. Все замечания были в основном архитектурные: добавление гибкости, расширение логирования.

Итоги конкурса

Давайте подведем небольшой итог в виде таблицы

Участник

Время

Качество кода

Моя личная оценка решения(очень субъективно)

Нейросеть

30 сек

Примитив, нет гибкости

3/10 Работает - и ладно. Но развивать страшно.

Стажер с чатом

5 минут

Тупая копипаста

1/10 Даже не пытался что-то исправить

Стажер без чата

2-3 часа

Паттерны, гибкость, абстракция

8/10 Для стажера отличная оценка, он проделал хорошую работу и показал самостоятельность. Горжусь!!

Мидл вайбкодер

1-2 часа

Разделение ответственности, но нет гибкости и расширяемости

5/10 Оно лучше, чем у нейросети, но есть свои проблемы.

Сеньор без нейросети

30-40 минут

Присутствует архитектура, разбиение на пакеты, логирование, константы, гибкий код.

9/10 Код лаконичный, просто, гибкий, читаемый.

10 не ставлю - всегда есть куда стремиться

Сеньор с нейросетью

10 минут

Все преимущества, что и без нейросетей

9/10 Все преимущества, что и без нейросетей

Итог

Нейросеть - это ускоритель. Он дает вам 5x к уму и скорости. Ваши знания, ваш опыт, ваше умение мыслить - это база. А нейросеть умножает эту базу.

Формула простая:

Ваш уровень * 5x = результат

Если ваш уровень - 0, то извините. 0 умножить на 5 равно 0. Профита вы не получите.

Если ваш уровень - 1 (вы новичок, который хочет разобраться), то 1 * 5 = 5. Уже что-то.

Если ваш уровень - 2 (вы уверенный джун), то 2 * 5 = 10. Вы уже далеко впереди.

А если ваш уровень - 10 (вы сеньор с горящими глазами), то 10 * 5 = 50. Вы бог.

Нейросеть не делает вас умнее. Она делает вас быстрее. Но если нечего ускорять - пользы особо не будет.

Проблема начинающих в том, что они пытаются заменить свой 0 на нейросеть. Но нейросеть не заменяет базу. Она умножает ее.

Так что сначала наберите свой уровень. Пишите код руками. Ошибайтесь. Ищите сами. А потом подключайте ускоритель.

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

Если вы уже давно в профессии, но в последнее время не пишете код руками - попишите, вспомните. Писать код - это классно. Особенно хороший код, который хочется поставить в рамочку.

А если вы пользуетесь нейросетью для помощи в нужные моменты или делегируете на нее рутину - велком в мой лагерь. Именно так я и пользуюсь нейросетью. Не так, что скармливаю все ТЗ и жду, откинувшись на стуле. Нет, я люблю писать код. Но писать DTO или миграции на создание таблиц я уже давно устал - пусть это сделает помощник. А что-то посложнее оставит мне!

Пользуйтесь нейросетями с умом. Всем спасибо!

Комментарии (22)


  1. Dhwtj
    19.06.2026 10:43

    Почему сейчас стажеры и джуны знают больше, но пишут код гораздо хуже, чем раньше

    План по валу?

    Вал по плану©

    Больше говнокода богу говнокода! ©


    1. MishaBucha Автор
      19.06.2026 10:43

      Хуяк хуяк и в продашкн)


  1. Dhwtj
    19.06.2026 10:43

    Итоговое решение получилось коротким, четким. Использовались абстракции, паттерны. 

    Паттерны и нейросеть может, это её работа. А вот мыслить на нужном уровне абстракций может только человек.


  1. Kot_na_klaviature
    19.06.2026 10:43

    Сейчас, в эпоху ИИ, найти информацию стало куда легче, чем раньше

    Не найти, а нагенерить. Найти это работа и обучение. А нагенерить это делегировать генератору. Уровень таких погромистов будет стремительно деградировать к нулю. А потом и нормальный бизнес (да и вообще люди) устанут от потока неконтролируемого нейрослопа и будут банить. Уже устают. Тот же Хабр после запрета нейроспама стало гораздо приятней читать.


    1. Metotron0
      19.06.2026 10:43

      Нейросети в поисковиках ищут. Даже дают ссылки, где они нашли.


  1. MountainGoat
    19.06.2026 10:43

    Потому что сейчас все начальники хотят, чтобы качесво как вручную, а объём как из ИИ без проверок.


  1. apidev
    19.06.2026 10:43

    Про архитектуру и абстракции - весьма спорно.

    Задача небольшая, инкапсулирована внутри одного класса.

    Засовывать N паттернов сразу считаю преждевременной оптимизацией, ибо работал с проектом, где таких абстракций накручено выше крыши и логики за кодом уже практически не видно.

    ИМХО, абстракции нужно добавлять в код по мере необходимости. Т.е. как только потребовалась доработка: мелкий рефакторинг и добавил нужную абстракцию.

    Особенно фиговым решением считаю своими руками писать синглтон и фабрику. Либо Guice, либо Spring IoC, либо jackarta.inject, ну или через ServiceLoader на худой конец, либо пинать сеньёра велосипедостроения. Иначе техдолговая яма наступит гораздо раньше.

    А джун с нейросеткой не теряет возможности развиваться. Либо самостоятельно (читая литературу), либо методом летающего ежа, на каждом Code Review. Бизнесу он пользу принёс, причём быстро.


    1. Dron007
      19.06.2026 10:43

      Согласен. Тем более, что синглтон многие вообще в антипаттерны записывают.


  1. Dron007
    19.06.2026 10:43

    Тут, в целом может быть и так, но вообще многое зависит от устоявшихся привычек, которые не факт, что будут полезны с приходом нейросетей. Например, одним из важных требований выдвигают поддержание кода. Очень хорошо это понимаю, отзывается болью от изменения плохо написанных проектов, неоптимальных решений. Когда кто-то быстренько написал что-то вроде работающее, а тебе потом надо что-то по мелочи изменить и ты тратишь в 10 раз больше времени, всё переписывая, это неприятно. Но это при условии что весь код пишет человек. Если код написала нейросеть и поддерживать тоже будет нейросеть, а если что, так и зарефакторит, то так ли уже важен этот критерий? Важны другие - производительность, стабильность. Они часто бывают связаны с поддерживаемостью. А иногда и наоборот - пишется что-то лучше поддерживаемое, но хуже в плане производительности. Руководителю же важны скорость разработки и просто выполнение задачи без сюрпризов в будущем. Но и с сюрпризами будет бороться нейросеть. Так что тут многое надо ещё переосмысливать в восприятии. Как многие до сих пор ожидают, что нейросети должны сразу писать идеальный код с первого промпта и такие "Ага-а-а, не может. На помойку её. Вот человек бы..." забывая, что любые сложные задачи итеративны и поэтапны.


    1. Kot_na_klaviature
      19.06.2026 10:43

      то так ли уже важен этот критерий?

      Это самый главный критерий. Ллм точно также нужен понятный и поддерживаемый код, как и человеку. Иначе она начнет путаться, исправлять одно и ломать другое. Для нее код это контекст. Если там лапша и тупость, она и будет продолжать писать лапшу и тупость. + неподдерживаемый нейрокод это сложное или даже невозможное ревью.


      1. Dron007
        19.06.2026 10:43

        Частично согласен. Если значение задано 20 раз в разных файлах, то более высока вероятность для современных моделей, что что-то упустит, хотя уже лучше справляется чем раньше. Но часто под поддерживаемостью понимается, что логика попроще, пусть и за счёт эффективности, наглядность повыше. То есть код строится в угоду именно ограничений интеллекта, а не из соображений эффективности. Даже вот выделение в функцию кода требует накладных расходов как правило - работа со стеком и прочее. Где-то можно и повторить пару раз с разными параметрами. Но человеку проще формировать модель восприятия так, а модели может быть и всё равно. В конце концов главная метрика - эффективность решения поставленной задачи. И если она решает эффективнее человека, то какая разница какой там код? Если он для эффективного решения объективно выгоднее когда структурирован, так он и будет становиться таким по мере улучшения обучения моделей.


        1. Kot_na_klaviature
          19.06.2026 10:43

          эффективность решения поставленной задачи

          Поддерживаемый код является существенной частью эффективности.

          если она решает эффективнее человека, то какая разница какой там код?

          Не знаю кто что решает и эффективнее какого человека, но зачем нужен читаемый код написал выше (путается ллм, невозможность в лапше делать ревью итд).


          1. Dron007
            19.06.2026 10:43

            Поддерживаемый код является существенной частью эффективности.

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

            LLM может запутаться и в хорошо поддерживаемом коде и неподдерживаемый это не обязательно лапша, это может быть просто сильно оптимизированное решение как раз.


  1. Matvey0808
    19.06.2026 10:43

    с нейросетью учиться вроде и тупо, а вроде и вся информация на ладони, но плюс искать все вручную в том, что мы еще 100 решений найдем которые нам не нужны и 100 раз прочитаем чужой код. И вот когда наступит момент когда мы наконец-то нашли решение своей проблемы, мы попутно собрали и многое другое, которое возможно, пригодится в будущих задачах.

    Но все же нужно приспособиться к нейронкам ибо это будущее, и без них никак уже в мире, как бы не было обидно.

    Ну вот даже когда мы готовимся к собесу с помощью ИИ и он нам выдает паттерны якобы самые частые, и в итоге мы по сути только запомнили шаблонные решения, наш мозг ничего нового не усвоил. Это деградация как по мне.
    Обучение не должно быть легким, если обучение легкое - это не обучение.

    Хотя кому я это говорю, я сам еще мал и глуп) полтора года мобилки изучаю от силы.

    Я кстати удивился, когда увидел решение мидла, не думал, что он OCP нарушит, раз он дошел до такого уровня.


  1. Snaret
    19.06.2026 10:43

    С посылом статьи на 100% согласен, к оформлению доказательств вопросы...

    КОД каждого участника в студию!
    А так субъективному оценочному суждению привык не доверять, уж извините.

    И мидла незаслуженно унизили) Ему задачки закрывать надо, быстро и более менее качественно. Ну нет у него 2-3 часа на неспешные раздумия, на таких как он IT держится.

    И по соотношениям время исполнения/качество/цена работы в час он явно будет впереди.

    А этому в принципе не верю (Сеньор с нейросетью 10 минут Все преимущества, что и без нейросетей 9/10 Все преимущества, что и без нейросетей).
    Нейросеть то та же. А значит выдаст тот же примитив. Ну и если сеньор за 9 минут 30 секунд реализовал все как боженька, то я Жанна Дарт Вейдер))))


    1. MishaBucha Автор
      19.06.2026 10:43

      Тут именно про то, что сеньор не просто увидел код и принял решение, а доводил его до приемлемого формата)

      Я со всеми созванивался и все смотрел, попрошу скинуть код всех, если интересует )


  1. Snaret
    19.06.2026 10:43

    Поразмышлял над вашей статьей еще раз.

    Оформил экспериментус. Дипсику скормил хороший промпт и вашу задачу.

    Вот что он мне выдал

    Скрытый текст
    import java.util.*;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    // ---------- Request DTO ----------
    class NotificationRequest {
        private final String requestId;
        private final NotificationType type;
        private final String recipient;
        private final String message;
    
        private NotificationRequest(Builder builder) {
            this.requestId = builder.requestId;
            this.type = builder.type;
            this.recipient = builder.recipient;
            this.message = builder.message;
        }
    
        public String getRequestId() { return requestId; }
        public NotificationType getType() { return type; }
        public String getRecipient() { return recipient; }
        public String getMessage() { return message; }
    
        public static class Builder {
            private String requestId;
            private NotificationType type;
            private String recipient;
            private String message;
    
            public Builder requestId(String requestId) { this.requestId = requestId; return this; }
            public Builder type(NotificationType type) { this.type = type; return this; }
            public Builder recipient(String recipient) { this.recipient = recipient; return this; }
            public Builder message(String message) { this.message = message; return this; }
            public NotificationRequest build() { return new NotificationRequest(this); }
        }
    }
    
    // ---------- Enum ----------
    enum NotificationType {
        EMAIL,
        SMS,
        PUSH
    }
    
    // ---------- Validator ----------
    interface Validator {
        boolean isValid(NotificationRequest request);
    }
    
    class EmailValidator implements Validator {
        @Override
        public boolean isValid(NotificationRequest request) {
            return request.getRecipient() != null && request.getRecipient().contains("@");
        }
    }
    
    class SmsValidator implements Validator {
        private static final int EXPECTED_DIGITS_AFTER_PLUS = 11;
    
        @Override
        public boolean isValid(NotificationRequest request) {
            String recipient = request.getRecipient();
            if (recipient == null) return false;
            if (!recipient.contains("+")) return false;
            int plusIndex = recipient.indexOf('+');
            String afterPlus = recipient.substring(plusIndex + 1);
            return afterPlus.length() == EXPECTED_DIGITS_AFTER_PLUS;
        }
    }
    
    class PushValidator implements Validator {
        @Override
        public boolean isValid(NotificationRequest request) {
            String msg = request.getMessage();
            return msg != null && !msg.trim().isEmpty();
        }
    }
    
    // ---------- Validator Factory ----------
    class ValidatorFactory {
        private static final Map<NotificationType, Validator> VALIDATORS = new EnumMap<>(NotificationType.class);
    
        static {
            VALIDATORS.put(NotificationType.EMAIL, new EmailValidator());
            VALIDATORS.put(NotificationType.SMS, new SmsValidator());
            VALIDATORS.put(NotificationType.PUSH, new PushValidator());
        }
    
        public static Validator getValidator(NotificationType type) {
            Validator validator = VALIDATORS.get(type);
            if (validator == null) {
                throw new IllegalArgumentException("Unsupported notification type: " + type);
            }
            return validator;
        }
    }
    
    // ---------- Sender ----------
    interface Sender {
        void send(NotificationRequest request);
    }
    
    class EmailSender implements Sender {
        @Override
        public void send(NotificationRequest request) {
            System.out.println("Sending EMAIL to " + request.getRecipient());
        }
    }
    
    class SmsSender implements Sender {
        @Override
        public void send(NotificationRequest request) {
            System.out.println("Sending SMS to " + request.getRecipient());
        }
    }
    
    class PushSender implements Sender {
        @Override
        public void send(NotificationRequest request) {
            System.out.println("Sending PUSH to " + request.getRecipient());
        }
    }
    
    // ---------- Sender Factory ----------
    class SenderFactory {
        private static final Map<NotificationType, Sender> SENDERS = new EnumMap<>(NotificationType.class);
    
        static {
            SENDERS.put(NotificationType.EMAIL, new EmailSender());
            SENDERS.put(NotificationType.SMS, new SmsSender());
            SENDERS.put(NotificationType.PUSH, new PushSender());
        }
    
        public static Sender getSender(NotificationType type) {
            Sender sender = SENDERS.get(type);
            if (sender == null) {
                throw new IllegalArgumentException("Unsupported notification type: " + type);
            }
            return sender;
        }
    }
    
    // ---------- Main Service ----------
    class NotificationService {
        private static final Logger LOGGER = Logger.getLogger(NotificationService.class.getName());
    
        private final Set<String> processedIds = ConcurrentHashMap.newKeySet();
        private int processedCount = 0;
        private int duplicateCount = 0;
        private int failedCount = 0;
    
        /**
         * Processes a single request: validates, checks for duplicates, and sends if valid.
         */
        public void send(NotificationRequest request) {
            // 1. Duplicate check
            if (request.getRequestId() == null) {
                // requestId null is considered invalid (will be counted as failed)
                // but we still need to process? We'll treat as failed, but not as duplicate.
                // However, duplicate check requires requestId; if null, we cannot track it.
                // According to spec: requestId == null is invalid, so we skip duplicate check.
                // We'll directly mark as failed.
                LOGGER.log(Level.WARNING, "Request with null requestId – marking as failed");
                failedCount++;
                return;
            }
    
            if (!processedIds.add(request.getRequestId())) {
                // Already processed
                duplicateCount++;
                LOGGER.log(Level.INFO, "Duplicate request skipped: {0}", request.getRequestId());
                return;
            }
    
            // 2. Common null checks (as per spec: requestId, recipient, message must not be null)
            if (request.getRecipient() == null || request.getMessage() == null) {
                failedCount++;
                LOGGER.log(Level.WARNING, "Request {0} failed: recipient or message is null", request.getRequestId());
                return;
            }
    
            // 3. Type‑specific validation
            Validator validator = ValidatorFactory.getValidator(request.getType());
            if (!validator.isValid(request)) {
                failedCount++;
                LOGGER.log(Level.WARNING, "Request {0} failed validation", request.getRequestId());
                return;
            }
    
            // 4. Send
            Sender sender = SenderFactory.getSender(request.getType());
            sender.send(request);
            processedCount++;
            LOGGER.log(Level.INFO, "Request {0} processed successfully", request.getRequestId());
        }
    
        /**
         * Processes a list of requests.
         */
        public void processAll(List<NotificationRequest> requests) {
            for (NotificationRequest req : requests) {
                send(req);
            }
        }
    
        /**
         * Prints the final statistics.
         */
        public void printStats() {
            System.out.println("Processed: " + processedCount);
            System.out.println("Duplicates skipped: " + duplicateCount);
            System.out.println("Failed: " + failedCount);
        }
    }
    
    // ---------- Main Demonstration ----------
    public class Main {
        public static void main(String[] args) {
            NotificationService service = new NotificationService();
    
            List<NotificationRequest> requests = Arrays.asList(
                    // Valid requests
                    new NotificationRequest.Builder()
                            .requestId("1")
                            .type(NotificationType.EMAIL)
                            .recipient("user1@mail.com")
                            .message("Hello email")
                            .build(),
                    new NotificationRequest.Builder()
                            .requestId("2")
                            .type(NotificationType.SMS)
                            .recipient("+79990001122")
                            .message("Hello sms")
                            .build(),
                    new NotificationRequest.Builder()
                            .requestId("3")
                            .type(NotificationType.PUSH)
                            .recipient("device-123")
                            .message("Hello push")
                            .build(),
                    // Duplicate of requestId "1"
                    new NotificationRequest.Builder()
                            .requestId("1")
                            .type(NotificationType.EMAIL)
                            .recipient("duplicate@mail.com")
                            .message("Duplicate")
                            .build(),
                    // Invalid: null requestId
                    new NotificationRequest.Builder()
                            .requestId(null)
                            .type(NotificationType.EMAIL)
                            .recipient("nullid@mail.com")
                            .message("null id")
                            .build(),
                    // Invalid: recipient null
                    new NotificationRequest.Builder()
                            .requestId("4")
                            .type(NotificationType.EMAIL)
                            .recipient(null)
                            .message("no recipient")
                            .build(),
                    // Invalid: email without '@'
                    new NotificationRequest.Builder()
                            .requestId("5")
                            .type(NotificationType.EMAIL)
                            .recipient("invalid-email")
                            .message("bad email")
                            .build(),
                    // Invalid: SMS without '+'
                    new NotificationRequest.Builder()
                            .requestId("6")
                            .type(NotificationType.SMS)
                            .recipient("79990001122")
                            .message("no plus")
                            .build(),
                    // Invalid: SMS with wrong length after '+'
                    new NotificationRequest.Builder()
                            .requestId("7")
                            .type(NotificationType.SMS)
                            .recipient("+123456")
                            .message("short")
                            .build(),
                    // Invalid: PUSH empty message
                    new NotificationRequest.Builder()
                            .requestId("8")
                            .type(NotificationType.PUSH)
                            .recipient("device-456")
                            .message("")
                            .build()
            );
    
            service.processAll(requests);
            service.printStats();
        }
    }


    Теперь вывод: у сеньора промпт лучше. За 10 минут он еще кофе с сигаретой прикончить успел.

    Полюбому


    1. MishaBucha Автор
      19.06.2026 10:43

      Оценку по времени давал не я, а они, так что чем он занимался остальное время, я не знаю))

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


  1. Wosk1947
    19.06.2026 10:43

    С посылом статьи согласен, но вот с аргументацией как будто бы неочень. Я по бумагам синьор-помидор, и на жабе уже лет 10 пишу в энтерпрайзах различной величины и степени кровавости. Если бы я сейчас получил такую задачу, то я написал бы решение как в самом первом листинге. Потому что уже не раз видел, до чего доходит вся ваша "архитектура" с чистым кодом. На проектах 10+ лет оно превращается в бесконечное месиво из абстракций, у некоторых сущностей десятки уровней наследования, фабрики фабрик и прочее чистокодовое нечитаемое говно. Вот когда надо будет масштабировать (спойлер, в 99% случаев оказывается не надо), вот тогда отмасштабирую. А сейчас - самое простое и тупое школьное решение, которое можно легко окинуть взглядом и понять за секунду имея одну извилину.


  1. EskakDolar
    19.06.2026 10:43

    А куда джунам деваться ? От них требуют умение пользоваться ИИ, иначе на работу не возьмут.


  1. FixicusMaximus
    19.06.2026 10:43

    На мок-собеседованиях ребята знают все, отвечают, будто они сами придумали этот термин или метод. Но как только дело доходит до самостоятельного кодинга - люди теряются.

    Зазубрить не значит знать, а знать не значит понимать. А что-то понимать в программировании без навыка работы с кодом очень сложно.


  1. Seyr
    19.06.2026 10:43

    Увы. Все всегда хотят побольше и подешевле. До ИИ это было невозможно.

    Сейчас ситуация изменилась. Один тратит 4 часа на задачу, второй справляется за 5 минут. Кого наймут? Даже если решение плохое, оно работает. Оно приносит деньги прямо сейчас.

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

    До ИИ не было джунов, способных решать задачи за 5 минут. Поэтому бизнес процесс был подстроен под осознание этого факта.

    Очень часто джун был бы рад, будь у него место в настоящей команде, где платят минимальные деньги (речь не о 30 000, а чтобы был свет, интернет и пачка сосисок с чаем) и ставили адекватные джуну задачи. Но он будет делать задачу 4 часа и практически всегда бизнесу выгоднее нанять оператора клода, а не классического джуна старого поколения. Классический джун со стороны бизнеса-это как платить за обучение студента без гарантии, что он останется работать у тебя. Вайбкодерский джун-это прото лоу грейд кодер, закрывающий задачи уже сейчас.