В своем блоге на Хабре мы уже не раз писали о Python. Хотя бы потому, что это один из наиболее популярных в мире языков программирования. В начале этого года по версии Tiobe он занял первое место. Популярность его объясняется достаточно просто - язык можно относительно быстро выучить на базовом уровне и начать двигаться к вершинам профессии. Но какие они, эти вершины? На что способен senior, какие задачи решает middle, а какие - junior? Об этом мы поговорили с Алексеем Некрасовым (@znbiz), лидером направления Python в МТС, программным директором направления Python, спикером профессии “Python-разработчик” в Skillbox и автором телеграм канала Python - учим и работаем.
Начнем с того, что должен знать junior, middle и senior
Конечно, все это усредненная информация, для разных случаев и мест работы ситуация может отличаться. Но база все же более-менее общая. Важный момент - в IT чем выше позиция разработчика, тем большим объемом soft-скиллов он должен обладать. Конечно, хард-скиллы тоже никто не отменял. Вот достаточно подробный список знаний и умений специалистов разного уровня.
Junior-разработчик:
hard: знать основы ЯП (языка программирования);
hard: иметь минимальные знания стека, используемого в продукте;
hard: уметь самостоятельно решать простые типовые задачи;
soft: адекватно воспринимать критику;
soft: помимо 8 часов работы самостоятельно дополнительно обучаться 2-3 часа в день.
Middle-разработчик:
hard: знать хорошо основной ЯП, в том числе его тонкости, ограничения, best practices;
hard: знать и применять шаблоны и стандарты промышленной разработки;
hard: уметь подбирать несколько решений нужной задачи и находить оптимальный вариант;
hard: знать и уметь работать со стеком, используемым в компании;
soft: быть наставником для менее опытных коллег;
soft: находить общий язык с тем, от кого зависит решение задач: аналитик, тестировщик, DevOps и другие разработчики;
soft: понимать требования бизнеса;
soft: оценивать свои силы и трудозатраты по своим задачам;
soft: новое качество (на удаленке). Тайм-менеджмент и умение разделять работу и личную жизнь.
Senior-разработчик:
hard: уметь быстро изучить и внедрить новый инструмент в продукт;
hard: прорабатывать архитектуру новых компонентов в продукте;
hard: смотреть на продукт шире и видеть все его ограничения, узкие места и т.д.;
soft: понимать, для чего создается продукт, и задавать направление дальнейшего его развития;
soft: проводить декомпозицию задач;
soft: делегировать задачи;
soft: быть наставником для middle разработчиков;
soft: переключаться между своей ролью и другими ролями в команде, к примеру иногда быть как аналитиком, так и DevOps;
soft: видеть сильные и слабые стороны менее опытных коллег, помогать команде стать лучше;
soft: новое качество (на удаленке). Тайм-менеджмент и умение разделять работу и личную жизнь.
Теперь - о задачах
Разработчики, используя указанные выше хард- и софт-скиллы, могут выполнять достаточно широкий спектр задач. Понятно, чем профессиональнее программист, тем более сложные задачи он может решать.
Junior. Если коротко, то основная задача junior разработчика - учиться и перенимать опыт у более опытных коллег. Часто обучение проходит в таком формате: брать все более сложные задачи (их даёт наставник или более опытный коллега), выполнение которых каждый раз будет требовать выходить за рамки того, что он знает.
Кроме того, он может и самостоятельно справиться с определенным кругом задач, не отвлекая других разработчиков. В основном это простые и типовые задачи:
Разработать валидацию входных/выходных данных,
“Причесать” код в соответствие с новым правилом из линтера,
Написать автотесты и т.п.
Но даже такие простые задачи проходят проверку у наставника, так как на них junior “набивает руку” и приучается к тем стилям разработки, которые используются в компании.
Вариант задачи для junior - написать тест на создание чего-либо. Например, слова:
from typing import Optional
from unittest import mock
import pytest
from app.factories import UserFactory
from app.models import Lexicon, User
from app.views import LexiconResourceList
from tests.conftest import deleted
@pytest.fixture
def user():
user = UserFactory()
yield user
deleted(user)
class TestLexiconResourceList:
f_post = LexiconResourceList().post.__wrapped__
@mock.patch('app.views.request')
def test_post(self, mock_request, user: User):
data = {'value': "Word_1"}
mock_request.json = data
mock_request.user = user
resp = self.f_post()
assert resp['value'] == data['value']
lexicon: Optional[Lexicon] = Lexicon.query \
.filter(Lexicon.value == data['value'], Lexicon.status == Lexicon.Status.active) \
.first()
assert lexicon is not None
# try again to add the word
resp = self.f_post()
assert 'This word already exists' in resp['errors'][0][0]
Middle-разработчик. Он - основная рабочая сила в команде, в среднем в компаниях на них приходится около 80% всех задач. Если junior делает что-то работающее, то задача middle - не просто сделать, "чтобы работало", но и выполнить свою её в оптимальный срок и качественно. Middle-разработчику чётко говорим, что нужно сделать и можно быть уверенным, что он это выполнит.
Пример задачи для middle: создать метакласс для реализации фабрик. Упрощённый вариант решения:
from typing import Union, List, Dict, Any
from faker import Faker
from app import db
from app.models import User
class ExceptionBeforeCreate(Exception):
pass
class ExceptionAfterCommit(Exception):
pass
class ExceptionBeforeCommit(Exception):
pass
class ErrorCreateObject(Exception):
def __init__(self, model, description, field_name: str = ''):
self.model = model
self.message = description
self.field_name = field_name
self.description = description
super().__init__(self.message)
class BaseFactory:
class Meta:
model = None
data = {}
def __new__(cls, *args, **kwargs) -> Union[List[Meta.model], Meta.model]:
new_kwargs = dict()
new_kwargs.update(kwargs)
fixture_data = cls.data
for name, val in fixture_data.items():
if name not in new_kwargs:
new_kwargs[name] = val()
try:
new_kwargs = cls.before_create(**new_kwargs)
except ExceptionBeforeCreate:
pass
new_object = cls.Meta.model(**new_kwargs)
new_object.save(commit=False, session=db.session)
try:
cls.before_commit(new_object)
except ExceptionBeforeCommit:
pass
db.session.commit()
try:
cls.after_commit(new_object, kwargs=kwargs)
except ExceptionAfterCommit:
pass
db.session.commit()
return new_object
@classmethod
def before_create(cls, **kwargs) -> Dict:
"""
Executes logic before the factory starts
:param kwargs:
:return: named parameters for object creation
"""
raise ExceptionBeforeCreate
@classmethod
def before_commit(cls, result_data: Union[List[Meta.model], Meta.model]) -> None:
"""
Executes logic after the object is created, but before the commission in the database
:return:
"""
raise ExceptionBeforeCommit
@classmethod
def after_commit(cls, result_data: Union[List[Meta.model], Meta.model], kwargs: dict = None,) -> None:
"""
Executes logic after data commit
:return:
"""
raise ExceptionAfterCommit
fake = Faker()
class UserFactory(BaseFactory):
"""
Factory for creating users
"""
class Meta:
model = User
data = {
'first_name': lambda: fake.first_name()[0:20].title(),
}
@classmethod
def before_create(cls, **kwargs) -> Dict[str, Any]:
data_for_create_user = dict()
data_for_create_user = cls.set_first_name(data_for_create_user, kwargs)
return data_for_create_user
@classmethod
def set_first_name(cls, data_for_create_user: Dict, kwargs: Dict) -> Dict:
name_field: str = 'first_name'
max_len_str: int = 20
value = kwargs.get(name_field)
if not value:
raise ErrorCreateObject(User, 'First name required', name_field)
value = str(value)
if len(value) > max_len_str:
raise ErrorCreateObject(User, 'The first name must be no more than 20 characters long.', name_field)
data_for_create_user[name_field] = value
return data_for_create_user
Senior-разработчик. Его основная функция в команде - принимать правильные решения по разработке продукта и направлять его технологическое развитие. Эта задача одна из самых ответственных, так как ошибки допущенные на этом уровне, могут очень дорого обойтись компании в будущем. Senior в силу своего опыта и знания бизнес-области на ранней стадии предотвращает крупные проблемы с минимальными затратами. Это разработчик, который смотрит в будущее и направляет команду в сторону создания идеального продукта, с помощью участия в проработке его архитектуры, декомпозиции бизнес-задач на технические задачи и их делегирование.
Задачу для senior просто так уже не показать в виде кода. Поэтому приведем приблизительные примеры тасков, которые могут упасть на senior-разработчика:
Нужно хранить иерархическую структуру сотрудников организации. При необходимости должно быть возможно:
максимально быстро выгрузить всю структуру руководителей конкретного сотрудника;
максимально быстро выгрузить всех подчинённых конкретного руководителя (не только прямых подчинённых).
Также стоит учитывать, что в данном примере используется реляционная СУБД, например, PostgreSQL.
При решении данной задачи senior может прийти к нескольким вариантам:
Добавить в продукт графовую базу данных.
Делать рекурсивные запросы в текущей БД при минимальных затратах на разработку сложного функционала.
Реализовать специальные алгоритмы, которые могут решить данную задачу в рамках РСУБД (реляционной СУБД). Например, вложенные множества.
На каких этапах разработки продукта подключаются кодеры разных уровней?
Сразу скажем, что разработчиков разной квалификации ищут не только и не столько в определенную компанию, сколько в конкретную компанию под конкретный продукт.
В зависимости от стадии развития жизненного цикла продукта, потребности в разработчиках отличается:
Стадия проверки гипотез. Когда задачи нужно решать в сжатые сроки и с минимальным количеством затрачиваемых ресурсов. Как раз в этом случае и нужен сеньор, поскольку он может не просто решить задачу, но и предложить оптимальное решение.
Разработка. Создание MVP (minimum viable product - минимально жизнеспособный продукт). На этом этапе закладывается ядро продукта. Конечно, наши читатели могут сказать, что MVP нужно выкидывать и всё писать заново и правильно, но сколько вы видели случаев, когда бизнес так делал? “Ничего не бывает настолько постоянным, как временное”, в случае разработки это высказывание ну очень актуально. И именно поэтому здесь нужны senior-разработчики, которые с очень высокой степенью вероятности сразу все сделают правильно. Если нанять специалиста с более низкой квалификацией, то, скорее всего, потом придётся переписывать большую часть кодовой базы проекта.
Наш собственный кейс - сеньоры проделали львиную часть работы в разработке одного сложного продукта - платформы для проведения торгов по товарам/услугам в реальном времени между b2b, b2c, c2b и т.д. Они заложили в фундамент важные свойства availability (доступность), performance (производительность) и modifiability (модифицируемость). Ну а все остальное достроили миддлы с джуниорами.
Выпуск продукта на рынок. Стадия перехода из MVP в production - в этот момент количество задач начинает расти. Данные задачи в будущем будут частью core составляющей продукта и для их реализации желательно искать senior-разработчиков, которые смогут заранее заложить качественную архитектуру. К сожалению, здесь есть одна проблема. Дело в том, чтобы найти одного хорошего senior-разработчика, нужно потратить не менее 2-3 месяцев. Но выход есть. Так, если в команде уже есть один или два сильных разработчика, то под их начало можно привлекать middle-разработчиков, которые закроют потребность на этой стадии.
Рост продаж. Стадия быстрого роста продукта. Тут впервые сталкиваемся с highload. Если заложили подходящий фундамент в виде продуманной архитектуры, соответствующей НФТ (не функциональным требованиям), то можем усилить команду разработчиками уровня middle. В противном случае у команды будут бессонные ночи.
Зрелость (насыщение рынка). К этому времени появляется много задач по техническому долгу плюс идёт оптимизация ресурсов. Появляется возможность взять на обучение разработчиков уровня junior и вырастить их под свой стек и продукт.
Но это картинка идеального мира. В реальности senior-разработчиков часто подменяют middle-разработчиками из-за малых бюджетов, сроков, отсутствия опытных ревьюеров, способных найти подходящих senior’ов.
Завершая статью, отмечу, что за прошедшие несколько лет разрыв в плане знаний/скиллов между разными категориями если увеличился, то не сильно. Что гораздо важнее - значительно усилились требования ко всем разработчикам. Если рассматривать juinior’а, то пять лет назад можно было знать только базовую часть языка без различных фреймворков, баз данных и т.д. Сейчас же с этими знаниями тяжело даже попасть на должность стажёра куда-либо.
Также про задачи и этапы роста разработчика я пишу в своём телеграм канале Python - учим и работаем.