Dev no Sekai | Михаил Гурбанов
152 subscribers
2 photos
16 links
Я — Михаил Гурбанов, тимлид и разработчик.
Здесь я пишу о мире разработки: от Python и инструментов для бэкэнда и ИИ до фронта с DevOps.

Делюсь опытом из практики, и заметками со спикерских выступлений

Tg: @mygurbanov
Github: https://github.com/insani7y
Download Telegram
Приветик! Я — Михаил Гурбанов, тимлид и разработчик.
Мой основной язык — Python, но на своем пути я успел заглянуть в DevOps, Typescript и Rust. Сейчас много занимаюсь приложениями на основе искусственного интеллекта и бэкендами, которые с ним работают.

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

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

Это Dev no Sekai — мир разработки и всего, что рядом с ней. Добро пожаловать
43
Channel name was changed to «Dev no Sekai | Михаил Гурбанов»
В последние годы все только и говорят про AI и LLM.

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

К счастью, сообщество не стоит на месте, и для Python есть отличный инструмент — pydantic-ai

Вот, с чем он может помочь:
• Гибкая конфигурация клиента — работает с разными провайдерами, от OpenAI до Grok, а также с кастомными inference серверами
• Гарантировать, что LLM ответит строго по заданной JSON-схеме, что помогает избегать галлюцинаций
• Предоставляет LLM возможность вызывать инструменты и ходить во внешние системы
• Дает удобный интерфейс для работы с LLM как с «клиентом»

В итоге с LLM становится удобно работать в привычной для разработчиков парадигме: чёткие контракты, предсказуемое поведение и простая интеграция в приложение или сервис.
4👎3
Продолжая истерию, вокруг LLM, нельзя обойти стороной протокол MCP. Его основная задача - дать возможность LLM выполнять произвольный код при помощи tool. В то же время этот протокол задает единый язык общения между LLM и приложением.

Тот же самый Cursor уже имеет в себе MCP-клиент, что позволяет ему обращаться к внешним серверам для выполнения дополнительной логики. Чтобы это работало, нужен MCP-сервер. Такой сервер можно поднять буквально в несколько строк с помощью fastmcp:

from fastmcp import FastMCP

mcp = FastMCP("Demo 🚀")

@mcp.tool
def add(a: int, b: int) -> int:
"""Складывает два числа"""
return a + b

if __name__ == "__main__":
mcp.run()


Теперь любая LLM с MCP-клиентом сможет вызвать этот add и получить результат в предсказуемом формате.

А вот так выглядит минимальный клиент, который подключается к серверу и вызывает инструмент:
from fastmcp import Client
import asyncio

async def main():
async with Client("my_server.py") as client:
tools = await client.list_tools()
print("Доступные инструменты:", tools)

result = await client.call_tool("add", {"a": 5, "b": 3})
print("Результат:", result.content[0].text)

asyncio.run(main())


Этот пример складывает два числа, но в реальных сценариях через MCP можно давать модели доступ к базе данных, API или внутренним сервисам. Главное — всё по единому протоколу, и это действительно удобно: теперь любой MCP-клиент сможет переиспользовать ту же самую логику.
👍52
Нашел еще один blazingly fast тайпчекер на rust - zuban

Как обычно все, что написано на rust быстрее, чем предыдущие решения во много раз (по крайне мере, так заявляют)
И это решение не исключение, разработчик заявляет, что zuban быстрее mypy в 20-200 раз
Я поглядел на бенчи и пока что меня терзает сильный скепсис в отношении этих цифр
Хочу погонять его на реальных проектах и посмотреть, что из этого выйдет

Но в целом выглядит довольно многообещающе, особенно с учетом вот этих тестов
По ним можно видеть, что zuban поддерживает практически то же самое, что и mypy
Хочу подетальнее рассмотреть это решение, так как это уже не первый тайп-чекер, написанный на rust, ведь ty уже с нами какое-то время, хоть он и в альфе
Возможно, пришло время поменять mypy на что-то побыстрее 🤔
👍7
Там, кстати, вышел Python 3.14.
Кратенько о том, что нового и прикольного в нем появилось.

PEP 750 — t-строки

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

Самый простой пример может выглядеть как-то так:
tmpl = t"Hello, ${name}!"
print(tmpl.substitute(name="Michael"))


Смысл в том, что строка не форматируется сразу.
Её можно передать в другой модуль, отложить подстановку или использовать свой шаблонизатор.
Это особенно удобно, когда шаблон — не просто текст, а часть логики. Такое особенно часто встречается при генерации кода.

CLIENT_TEMPLATE = t'''
class SomeClient:
def __init__(self, base_url):
self.base_url = ${base_url}

def ${method_name}(self, payload):
return requests.${http_method}(f"{self.base_url}/${endpoint}", json={param_name})
'''

generation_config = {
"base_url": "https://whatever.com",
"method_name": "create_user",
"endpoint": "users",
"http_method": "post",
}

# И вот тут получается готовый клиентский код
CLIENT_TEMPLATE.substitute(**generation_config)

Раньше для этого приходилось хранить строки вручную, вызвать .format(), следить за фигурными скобками и экранированием.
Но теперь за этим будет следить сам Python, тк шаблон - это не просто строка, а специальный объект.

Можно динамически собирать куски кода, генерировать Python-функции, SQL-запросы или промпты для LLM — не превращая всё это в нечитабельные строки.

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

PEP 649 + PEP 749 — ленивые аннотации

До Python 3.14 аннотации вычислялись сразу, и если сослаться на класс или тип, которые ещё не определены, то всё падало.
Например:

class User:
def __init__(self, profile: Profile):
self.profile = profile

class Profile:
def __init__(self, user: User):
self.user = user


Такой код раньше выдавал: NameError: name 'Profile' is not defined.
Приходилось писать костыли — from __future__ import annotations или аннотировать через строки вот так: "Profile".

Теперь это больше не нужно.
Python сохраняет аннотации в виде исходного текста и вычисляет их только тогда, когда они реально понадобятся — например, при проверке типов, в Pydantic, IDE или еще где.

То есть аннотации больше не влияют на рантайм до тех пор, пока к ним не произойдет обращения.
Теперь при определении класса Python не пытается вычислить Profile — он просто помнит, что там аннотация 'Profile'.
И только когда произойдет обращение к User.__annotations__, Python аккуратно развернёт это в реальный типы.

Это нововведение дает:
• Меньше циклических импортов
• Более быстрый старт модулей
• Предсказуемое поведение при анализе типов

Это далеко не все, что принес нам Python 3.14, на остальное тоже посмотрим!
👍53
В продолжение о том, что нам даёт свежевышедший Python 3.14

PEP 779 — Free-Threaded Python

В 3.13 можно было собрать Python без GIL вручную, но теперь это официально поддержано.
Сборка без глобальной блокировки позволяет потокам действительно выполняться параллельно, а не по очереди под контролем GIL.

Что это значит на практике:
• CPU-bound задачи теперь можно распараллелить обычным threading, без multiprocessing и сериализации данных между процессами
• Классические примеры вроде работы с большими матрицами, парсинга или рендеринга теперь могут масштабироваться по ядрам

import threading

def calc(x):
# что-то сложное на математическом
...

threads = [threading.Thread(target=calc, args=(10_000_000,)) for _ in range(8)]
for t in threads: t.start()
for t in threads: t.join()


Раньше такой код давал почти нулевой прирост — теперь он реально распределяет нагрузку.
Главное ограничение — не все расширения C готовы к новой модели, так что экосистема некоторое время будет адаптироваться. Поэтому не ждите, что все сразу заработает на сборке без GIL (скорее всего все у вас разломается)

PEP 734 — Multiple Interpreters

Теперь можно запускать несколько независимых интерпретаторов Python в одном процессе — со своей памятью и модулями.

Это что-то между процессами и потоками.
Главная идея — изолировать код, не платя за межпроцессное взаимодействие и сериализацию данных, как в multiprocessing.

Что это даёт:
• Можно параллельно выполнять независимые задачи, не деля общие модули и состояние
• Подходит для запуска чужого или потенциально «грязного» кода внутри одного сервиса

from concurrent.interpreters import InterpreterPoolExecutor

def run_task(x):
import math
return math.sqrt(x)

with InterpreterPoolExecutor(max_workers=4) as pool:
results = pool.map(run_task, range(10))


Тут каждый run_task выполняется в своём мини-интерпретаторе. Никаких конфликтов, утечек или shared-state — всё изолировано.

Ну теперь почти как в go, осталось добавить удобный keyword для запуска таких интерпретаторов и можно больше не задумываться о том, чтобы сменить язык 🤡
Но в целом можно сказать, что это плюс для языка, все-таки в каком-то смысле Python и правда становится быстрее
👍6
OpenAI выпустила ChatGPT Atlas — новый браузер со встроенной GPT-моделью.

Что умеет:
• Чат прямо в боковой панели — можно обсуждать текущую страницу (ничего нового, но теперь нативно)
• Agent Mode — ассистент сам действует на сайте: переходит по ссылкам, кликает, ищет, оформляет
• Внешне — Chrome, но по сути — фундамент для веб-приложений, где интерфейсом управляет ИИ

Что это значит для разработчика:
• Браузер превращается из «окна» в интерфейс к знаниям и действиям, которыми модель реально управляет
• Выглядит так, что надо будет заниматься не только SEO оптимизацией, но еще и AI-оптимизацией сайтов. Опять работа, да
• В общем, скоро у топ-менеджмента начнёт ехать кукуха, а мы будем пилить AI-driven фронтенды

Но выглядит прикольно, мне нравится, попробую попользоваться
🤓5
ChatGPT 5 потерял более 65% капитала в инвестиционном эксперименте nof1, в то время как полностью бесплатный DeepSeek 3.1 дал плюс 10% буквально за пару дней!
В рамках эксперимента всем популярным нейронками дали по дал по $10 тысяч с возможностью делать случайные сделки
Результаты же сделок можно видеть на онлайн-графиках
Помимо DeepSeek в плюс также вышли Grok 4 и Qwen 3 MAX
А Claude, Grok и Gemini также в минусе, как и ChatGPT

Ну вот, теперь можно трейдить через DeepSeek, чтобы оплачивать подписку на ChatGPT 🫠
😁6😱1
Потихоньку начинаю поглядывать в сторону фреймворков, которые поддерживают протокол A2A (Agent-to-Agent) — когда агенты могут общаться между собой самостоятельно для достижения поставленной цели.

До недавнего времени удобных вариантов особо не было.
В pydantic-ai можно было поднять сервер, но клиента не было — говорить всё равно не с кем.
А в a2a-python поддержка вроде как есть, но интерфейс не супер-удобный, да и весь код синхронный.

И вот теперь в AG2 (autogen 2) тоже завезли поддержку A2A 👉 релиз 0.10.0

Теперь можно:
• поднять сервер агента с А2А интерфейсом
• подключиться к нему как к удалённому агенту
• заставить их общаться друг с другом по протоколу А2А

from autogen import ConversableAgent, LLMConfig
from autogen.a2a import A2aAgentServer, A2aRemoteAgent

agent = ConversableAgent(
name="python_coder",
system_message="You are an expert Python developer...",
llm_config=LLMConfig({"model": "gpt-4o-mini"}),
)

# поднимаем сервер
server = A2aAgentServer(agent).build()

# подключаемся как к удалённому агенту
remote = A2aRemoteAgent(
url="http://localhost:8000",
name="python_coder",
)


Выглядит как первый по-настоящему удобный способ строить распределённые агентные системы, где каждый агент — отдельный сервис, говорящий на общем языке.
А вот тут лежит дока, можно поглядеть.
🔥103
Не люблю писать тесты.
Мечта любого разработчика: пока ты пьёшь кофе — ИИ сам написал тесты, CI зелёный, жизнь удалась. Но как обычно, дьявол в деталях.

Уже существует немало решений, которые умеют генерить тесты на основе LLM или без нее, тут посмотрим на самые рабочие.

⚙️ Pynguin — мутации ради покрытия
Pynguin анализирует код, типы и зависимости, а затем подбирает входные данные, чтобы пройтись по всем веткам выполнения. Что-то вроде hypothesis, только без вас.
Допустим, у нас есть модуль my_module.py:

def add(a: int, b: int) -> int:
return a + b

def divide(a: int, b: int) -> float:
if b == 0:
raise ValueError("division by zero")
return a / b


Теперь запускаем генерацию тестов:
pynguin \
--project-path ./my_project \
--output-path ./tests/generated \
--module-name my_module


Pynguin создаст в tests/generated что-то вроде:

def test_addition_works():
result = my_module.add(2, 2)
assert result == 4

def test_divide_by_zero():
with pytest.raises(ValueError):
my_module.divide(1, 0)


Покрытие — есть и вроде бы все здорово. Смысл — зависит от исходного кода. Но вот если бы, например, divide() тихо возвращал None вместо исключения, Pynguin всё равно зафиксировал бы это в тесте как нормальное поведение, хотя это очевидный баг. И вот в вашей кодовой базе есть тесты, которые делают только хуже.

🕵️ Keploy — тесты через подслушивание
Пример: у вас есть простой FastAPI-сервис.
from fastapi import FastAPI

app = FastAPI()

@app.get("/hello/{name}")
def hello(name: str):
return {"message": f"Hello, {name}!"}


Дальше вы запускаете приложение под контролем Keploy:
keploy record --command "uvicorn main:app"


Keploy поднимет приложение, начнёт слушать трафик и запишет все ваши действия — например, когда будет сделан запрос, его результат будет сохранен в YAML-файле. Этот YAML-файл будет считаться эталонным и не его основе будут производиться проверки при запуске тестов.
name: Test-hello-endpoint
request:
method: GET
url: /hello/Alice
response:
status: 200
body: '{"message":"Hello, Alice!"}'


Далее можно прогнать сохранённые сценарии и сравнить, совпадают ли ответы с тем, что сохранено у нас в сгенеренных файлах. Если что-то не совпадает — тест упадёт.
Плюс — язык неважен, работает с любыми стековыми связками. Минус — сложнее встраивать в защищённые среды, тк эта штука любит ходить в облако.

Выглядит так, что сейчас автоматическая генерация "хороших" тестов скорее не “магическая кнопка”, а поле для экспериментов: можно поиграться локально, посмотреть, что получится, но рассчитывать на безболезненную интеграцию в CI/CD пока рано.

Тем не менее, направление интересное — как минимум, оно заставляет нас задуматься, что именно мы проверяем тестами и кто ещё может это сделать за нас.
👍6
Выходные в самом разгаре — а у меня уже руки чешутся потрогать что-то новое и прикольное.
Недавно наткнулся на MCP Market — платформу, где можно найти MCP-сервер почти под любую задачу.

💡Что это вообще такое
MCP (Model Context Protocol) — протокол, с помощью которого можно подключить LLM-ассистента к реальным инструментам: git, API, файлам, CI/CD, таск-трекерам.

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

🔧 Несколько интересных примеров
— Хочешь залететь в open source, но не дружишь с git?
Тогда GitHub MCP Server поможет работать с GitHub API на естественном языке: коммиты, пул-реквесты, ревью — всё из диалога с ассистентом.

— Для любителей no-code full-swag n8n тоже есть решение - n8n MCP Server. Вам даже не придется погружаться в то, что такое n8n, эта штука позволяет создавать сценарии, запускать воркфлоу и автоматизировать процессы без кликов в интерфейсе.

— А если ты уже давно перетаскиваешь таски внутри таск-трекера (соболезную, понимаю) — посмотри на Task Master.
Этот сервер даёт возможность управлять туду-листами и задачами прямо через чат с ассистентом.

И самое приятное — все эти серверы доступны бесплатно.
Так что если вы давно хотели, чтобы ассистент умел не только болтать и отвечать на вопросы, но и работать вместе с вами — самое время собрать свой набор MCP-сервисов.
Пусть теперь он делает рутину, а вы — интересное.
🔥91
Dev no Sekai | Михаил Гурбанов
ChatGPT 5 потерял более 65% капитала в инвестиционном эксперименте nof1, в то время как полностью бесплатный DeepSeek 3.1 дал плюс 10% буквально за пару дней! В рамках эксперимента всем популярным нейронками дали по дал по $10 тысяч с возможностью делать случайные…
Там, кстати, завершили этот эксперимент и вот результаты

По итогу победил Qwen3 Max и заработал 500$

В самом низу топа оказались модели Grok 4 и ChatGPT 5, им доверять денежки я бы не стал 🤡

Вот вам и рецепт бесплатных денежек, если, конечно, у вас в доступе есть Qwen3 Max
🤡2
Чем старше проект — тем больше автоматизаций спасают нервы.
И если вы уже устали писать скрипты “на коленке”, самое время познакомиться с n8n.

💡 Что это
n8n — open-source альтернатива Zapier, только с возможностью писать свои ноды на JavaScript и запускать всё у себя.
Можно соединять API, базы данных, LLM-модели, GitHub и почти всё, у чего есть REST или SDK.

🛠 Примеры, где n8n действительно удобен:
— ML-пайплайны: забирать результаты из S3, триггерить постобработку и уведомлять в Telegram.
— RAG-системы: обновлять векторный индекс после каждого пуша в Git.
— Боты и интеграции: от напоминаний о новых PR до сборки внутренних отчётов
— И любые другие автоматизации

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

🎯 Почему это становится особенно интересно сейчас
С появлением протоколов вроде MCP и A2A, n8n превращается в полноценный инструмент для построения агентных систем.
Любой агент — это по сути API, а n8n умеет работать с API так, как будто создан для этого.

В итоге: n8n — не полностью no-code, а скорее less-code, так как в каких-то случаях инструментов из коробки может и не хватить.
С точки зрения храненя проекта подойдет тот же самый gitlab/githab/whatever. Флоу простой, вы натыкиваете флоу через интрефейс, экспортируете его в yaml или json и кладете в репозиторий. Можно даже сделать так, чтобы эти флоу сами сохранялись в нужный для вас репозиторий (да, через тот же самый n8n)
В общем, n8n — это когда ты всё ещё инженер, просто теперь автоматизация занимает минуты, а не дни.
Скоро принесу пару примерчиков автоматизаций из жизни ⚡️
🔥92
Litestar: фреймворк, который тихо стал нормальной альтернативой FastAPI

Litestar — фреймворк, который появился абсолютно незаметно для python-мира, но при этом уже готовится стать заменой FastAPI.
Этот фреймворк мы заметили еще в далеком 23-м году, когда никто ничего он нем не знал. Но он нам очень сильно понравился и вот почему:

1. Человеческий DI

В Litestar зависимости — это обычные функции.
Не классическая FastAPI-магия.

def create_awesome_service():
return YourAwesomeService(...)

@get("/", dependencies={"awesome_service": Provide(create_awesome_service)})
async def list_something_cool(awesome_service: YourAwesomeService):
return await awesome_service.list_very_cool_things(...)


Прозрачно, тестируемо и самое главное - понятно. Более того, его можно комбинировать с такими библиотеками, как: dishka, that-depends и даже старый-добрый dependency-injector.

2. Контроллеры, которые не превращают проект в мешанину

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

class UserController(Controller):
path = "/users"

@get("/")
async def list_users(self):
return [{"id": 1, "name": "Alice"}]


Когда сервис растёт — проект остаётся читаемым.

3. Нормальные DTO (того, чего FastAPI очень не хватало)

DTO в Litestar позволяют разорвать связку
ORM → API → запросы → ответы.

То есть можно отдельно описывать:

• Что приходит в запросе
• Как выглядит модель внутри сервиса
• Что именно отдаёшь наружу

# Моделька пользователя на алхимии
class UserModel(sqlalchemy.orm.DeclarativeBase):
email: orm.Mapped[str]
name: orm.Mapped[str]
password_hash: orm.Mapped[str]

# Данные для создания пользователя
class CreateUserRequest(pydantic.BaseModel):
email: str
name: str
password: str

# Вот эту штуку мы вернем через API после создания
class UserReadDTO(DataclassDTO[User]):
# Чем-то похоже на секцию exclude в DRF
config = {"exclude": {"password_hash"}}

@post("/users", dto=CreateUser, return_dto=UserReadDTO)
async def create_user(data: CreateUserRequest) -> UserModel:
user = UserModel(...)
return user

Да, оно под капотом переделает модельку алхимии и вернет нам json, который содержит все поля из UserModel, кроме password_hash, которое мы исключили в конфиге DTO.
Очень удобно, такой подход позволяет нам меньше задумываться о том, как описать ту или иную схему и больше сосредоточиться на бизнес-логике.

4. Advanced Alchemy: приятная ORM-интеграция

Вместо того чтобы прикручивать SQLAlchemy вручную, Litestar отлично работает с Advanced Alchemy — современным слоем над SQLAlchemy, где уже есть:

• Асинхронные сессии
• Репозитории и CRUD
• Жизненный цикл сессии, привязанный к запросу
• Интеграция с DTO

Самое важное — не нужно оборачивать SQLAlchemy в самописный сервисный слой: всё уже готово.
Например, вот так вы можете сделать репозиторий, в котором уже будут все необходимые CRUD методы.

from advanced_alchemy import SQLAlchemyAsyncRepository

class UserRepository(SQLAlchemyAsyncRepository[UserModel]):
model_type = UserModel


Да, вот так просто, без огромной кучи самописных SQL запросов и боли.

Небольшой итог:

В принципе, Litestar позволяет делать то же самое, что и FastAPI, но лучше 😁

• Простой и понятный DI
• Контроллеры, которые легко масштабируются
• Неплохие DTO
• Хорошая интеграция с Advanced Alchemy

Кстати, вот тут делали бенчмарки FastAPI, Litestar и других разных фреймворков. Litestar очень даже шустрый (да, шустрее FastAPI)!
Так что если вы задумываетесь о том, что бы еще такого прикольного попробовать на Python, очень рекомендую данный фреймворк.

P.S.
Чуваки даже написали гайд, как мигрировать с FastAPI.
🔥11👍3🏆1🆒1