Как стать автором
Обновить
38.32

Совершенный код *

Как Макконнелл завещал

Сначала показывать
Порог рейтинга

Как писать REST API — 9 правил

REST API (Representational State Transfer Application Programming Interface) — это архитектурный стиль взаимодействия между клиентом и сервером через HTTP. Он определяет принципы построения API, обеспечивая стандартизированный и эффективный обмен данными между различными системами.

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

  2. POST запросы для загрузки/создания сущностей, PUT для изменения в сущности, а PATCH - для мелких изменений в сущности.

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

  4. Иерархия. Нужно соблюдать единую иерархию для построения запросов. Конструкции /users/1/friends/3/items/4 будет неудобно использовать разработчикам клиентских приложений. Нужно избегать такую иерархию, нужно конкретно указывать что нам нужно, не перезагружать данными.

  5. Соблюдайте версионирование API. Допустим: /api/v1/get_all_users и /api/v2/get_all_users. При выходе новой версии не всегда разработчики готовы переписать логику клиентских приложений, и для этого нам нужно разделять версии API

  6. Чувствительные данные. Очень важный момент. Например, передача различных паролей в Query-параметрах запрещено, только в теле запроса.

  7. Из 6 вытекает 7е правило: не логируйте персональные и прочие секретные данные. Было несколько случаев, когда из-за логирования параметров утекали данные пользователей.

  8. Расширение ответов. Во-первых, не передавайте массивы в самом верхнем уровне ответа. JSON-ответ от сервера не должен быть массивом, только словарь (ключ-значение). Если мы будем возвращать список, в будущем нам будет неудобно расширить ответ сервера, добавив дополнительные параметры. А также иногда в API разработчики делают обязательные параметры, например помимо самих данных параметр success и error, чтобы клиентское приложение могло понять, что произошла ошибка, или наоборот, успешно выполнилось.

  9. Работа с датой и временем. Есть два метода - использование Unix Timestamp (количество секунд прошедших с 1 января 1970 года). Это в какой-то степени более удобно для машины, но менее удобно для клиентского разработчика. И второй метод как раз делает формат даты и времени более понятным - это ISO 8601 (2025-04-05T12:00:00.100Z). Этот метод более удобный, т.к. мы понимаем дату и часовой пояс, точное время.

  10. Бонусное правило, лично мое. Относится не только к REST API, но и в принципе к программированию. Пишите код так, как будто эта поэма, книга. Попробуйте сами поработать со своим API, будто вы его не писали. Разделение ответственности, единая точка отказа, Don't Repeat Yourself и даже Keep It Simple, Stupid. Все это конечно-же вариативно.

Теги:
+4
Комментарии5

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

Вот продукт готов, покупатели и менеджеры довольны... Они твои пользователи.

Вопрос: ты никого не забыл? Кто первым посмотрит на твой продукт еще до выхода его в прод? Кто увидит его в сыром, первозданном виде? Кому придется поддерживать его, исправлять ошибки?

Да, да.... это я, твой коллега, твой первый пользователь, твой продукт - это код, я им пользуюсь.

Почему ты не подумал обо мне: не написал README, не оставил инструкций, не озвучил подводные камни? Почему мне нужно провести реверс-инжинириг, просто что бы запустить или задеплоить проект?

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

Почему ты злишься, когда я комментирую код на ревью? Почему ты отвечаешь - "мне так удобно"? Почему... почему... почему?

После этого, ты удивляешься тому, что я не хочу иметь ничего общего с твоим продуктом и с тобой. Почему?

Теги:
Всего голосов 6: ↑5 и ↓1+5
Комментарии4

Регулярно на Хабре выходят статьи с рекомендацией использовать moment.js. В комментариях обязательно начинают советовать какой-нибудь dayjs или js-joda, но не потому, что они чем-то сильно лучше, а потому, что первый задепрекейчен авторами.. в пользу luxon.

Что за мания такая у JS-еров использовать раздутые тормозные библиотеки? Есть же быстрый и миниатюрный $mol_time с гораздо более удобным и функциональным API, почти полностью поддерживающим ISO8601, в отличие от всех остальных библиотек.

Бенчмарки говорят сами за себя
Бенчмарки говорят сами за себя

Что мотивирует людей довольствоваться не самым лучшим решением в индустрии? Я, наверно, странный, но я не могу этого понять.

Теги:
Всего голосов 19: ↑7 и ↓12-3
Комментарии39

Должен же тимлид смотреть Merge Request (Pull Request)? 

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

  • контролировать качество кода программистов команды;

  • следить за соблюдением принятых стандартов;

  • управлять рисками кодовой базы команды;

  • обучать участников команды через ревью их кода;

  • держать руку на пульсе того, чем занимаются участники команды. 

Однако что делать, если у вас кросс-функциональная команда, состоящая из двух бэкендеров, пары фронтендов, QA и аналитика? Нужно ли вам просматривать все их MR? Сможете ли вы адекватно оценить код на PHP, код на React + TypeScript и автотесты на Python? Очевидно, что нет. 

Для разрешения данной ситуации вы можете:

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

Попросить разработчиков проводить код-ревью друг у друга. Однако всё довольно быстро превратится в формальные проверки, когда одобрения ставятся просто ради галочки.

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

 Как поступить?

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

Максимально возможное покрытие фитнес-функциями (автоматические форматтеры, автотестеры, анализаторы кода). Согласуйте с техлидами внедрение в проект автоматических анализаторов и добавьте их в пайплайны репозитория. Ни один MR не пойдёт на ревью до тех пор, пока не будет соответствовать установленным стандартам. Так вы сэкономите массу времени.

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

То есть программисты из других команд могут не разбираться в вашем домене, но они способны проверить, что код не делает ничего лишнего и корректно функционирует в рамках проекта (при нагрузке, сущностях, подходах). А факт рандомности ревьювера и фиксации апрува, не позволит им проводить ревью формально.

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

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

P.s. Рекомендую: Эволюционная архитектура - автоматизированное управление программным обеспечением - Нил Форд`

Теги:
Всего голосов 1: ↑1 и ↓0+1
Комментарии1

Ну что, народ, что называется "тащусь" от новой сегодняшней версии ChatGPT CodeCopilot.

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

Мне же особенно понравилась опция Code Review. Это круто! Типа он проходит по коду и даёт рекомендации.

For example:

I've reviewed the code and suggested improvements for logical consistency, potential crash prevention, layout calculations, and readability. Let me know if you need further refinements! 🚀

Даже не знаю, как это назвать. Реально круто!

Теги:
Всего голосов 8: ↑4 и ↓4+2
Комментарии1

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

Я думаю, что если ваши первые годы прошли удачно, то есть вы попали в профессиональную команду с хорошей инженерной культурой, то за 3 года вы выйдете на уровень, когда старый код будет выглядеть нормально для тех задач и тех условий в которых он был написан. Да любой код устаревает это правда, но легаси не означает что код плохой, просто изменились обстоятельства. Этот процесс может занять и больше времени, но если после 5 лет разработки старый код по прежнему выглядит плохо, то здесь явно что-то надо менять в профессиональном плане.

Смотря назад, я понимаю что поворотным моментом в моей карьере стал период, когда я увлекся функциональными языками и курсом СИКП, по которому потом во многом строилось обучение на Хекслете. Произошло это так, я видел что вокруг меня, многие профессионалы, говорят и используют функциональные языки и какие-то связанные с этим подходы. В тот момент речь шла про erlang, scheme/racket (для сикпа), clojure и haskell, которые я в разной степени начал изучать. На эрланге даже получилось сделать проект codebattle.hexlet.io, который потом, спустя много лет переписали на elixir (это опенсорс).

Изучение этих языков многое перевернуло в моей голове и дело даже не в том что они функциональные, а в том, что в среде программистов на этих языках поднимались вопросы, с которыми я раньше не сталкивался. Я понял что смотрел на разработку не на том уровне. Весь мой предыдущий опыт был скорее в стиле вот чистый код, вот мартин, вот фаулер, вот ООП, вот паттерны. Как оказалось, несмотря на здравые мысли в этой части, все же это была оболочка, а не фундамент. А фундамент лежал в таких областях как управление состоянием, изоляция побочных эффектов, конечные автоматы, statefull/stateless части системы и тому подобное.

Во время чтения и решения книги СИКП я реализовал свое ООП, полностью поняв смыслы и как оно работает, в чем соль динамической диспетчеризации особенно множественной, чего не рассказывают в обычных книгах по ООП. Оказалось, что многое о чем пишут в классике (аля книги по java), это лишь поверхностные вещи или особенности работы конкретных языков, а не фундаментальные концепции.

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

p.s. Делюсь опытом программирования в своем телеграм-канале организованное программирование

Теги:
Всего голосов 5: ↑4 и ↓1+3
Комментарии1

Функциональное ядро, императивная оболочка

Пожалуй, самый фундаментальный принцип прикладного программирования. Концепция очень простая, код с логикой нужно оставлять как можно более чистым, вынося побочные эффекты наружу в начало и конец выполнения программы.


Предположим, что мы создаем линтер для проверки исходного кода на соответствие стандартам кодирования. Какие побочные эффекты возможны в случае линтера? В первую очередь это чтение файлов с исходным кодом. Во вторую может быть запись, если линтер умеет автоматически исправлять ошибки. Весь остальной код, по сути, чистый, так как проверка на соответствие правилам не меняет ничего в окружающем мире. И этого кода подавляющее большинство в исходниках линтера. Как бы в таком случае мог работать программный код линтера? Что то в таком духе

const linter = new Linter(/* сюда передаем набор правил */);
const result = linter.lint('список файлов');

Такой код вполне допустим, но он смешивает побочные эффекты с логикой работы. Почему это проблема?

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

  • Код завязан на файлы, хотя смысловая часть линтера не работает с файлами, она работает со строками. Мы не сможем просто так подключить туда любой другой источник, например сеть. В случае js мы не сможем запустить линтер в браузере.

  • Работа с файлами сразу добавляет задачу по работе с файловыми исключениями

Всего этого можно было бы избежать, если бы побочные эффекты были вынесены за пределы ядра:

const linter = new Linter(/* сюда передаем набор правил */);
const filesData = readFiles(); // С учетом исключений и добавлением метаданных
const result = filesData.map((data) => linter.lint(data));

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

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

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

p.s. Делюсь опытом программирования в своем телеграм-канале организованное программирование

Теги:
Всего голосов 6: ↑6 и ↓0+8
Комментарии1

Доклад Особенности фреймворка $mol (+ слайды) с PiterJS #72.

О фичах $mol, которых нет в других фреймворках, и о том, зачем они нужны.

Автор - Станислав Яременко. Герой Hyper Dev, Синьор $mol-разработчик.

Писал на Vue, Svelte. Пробовал и другие фреймворки. Как-то раз я загуглил "лучший ui фреймворк", и на первом месте в выдаче оказался $mol. Конечно, я не поверил и начал разбираться...

Теги:
Всего голосов 12: ↑5 и ↓70
Комментарии14

Луковая архитектура, и как не заплакать в процессе погружения. Запись митапа

Суть лукового архитектурного подхода (Onion Architecture) — в разделении системы на несколько слоёв, каждый из которых выполняет свою функцию. Такой подход помогает сделать код более чистым и читаемым, обеспечивает удобное разделение бизнес-логики, домена приложения и инфраструктуры.

На внутреннем митапе рассказали, как устроена луковая архитектура, границах применимости и о том, как с пользой начать использовать её в разработке. Затронули теорию и практику: рассмотрели специфику луковой архитектуры на примере конкретного pet-проекта. 

Коснулись CQS-CQRS дизайн-паттерна, рассмотрели пайплайн MediatR, реализацию валидаций с FluentValidation, маппинг через AutoMapper, и как организовать сущности доменного слоя. 

Остановились на первых двух слоях — Domain.Core и Application. Об устройстве других слоев (Infrastructure и Web API) поговорим на будущих митапах.

Посмотреть запись митапа можно на YouTube или RUTUBE.

P.S. Это запись внутреннего митапа ИТ-команды Сравни — публикуем эпизоды без NDA, но (надеемся) с пользой для внешнего сообщества.

***

Узнавать о выходе наших новых материалов (митапов, лекций, статей), помимо Хабра, можно в тг-канале инженерного сообщества Сравни

Теги:
Всего голосов 10: ↑10 и ↓0+12
Комментарии0

Разбор доклада Артёма Арутюняна про Архитектуру и Реактивное Программирование

Топ перлов:

  • Реактивное программирование - это программирование на событиях.

  • Реактивность нужна между модулями, а внутри - не нужна.

  • По одному лишь графу связей можно понять правильно произведена декомпозиция или нет.

Мета: Копилка благодарностей, Новости, Форум

Теги:
Всего голосов 7: ↑4 и ↓3+1
Комментарии9

Препарируем React и находим родовые травмы

Выбор двух миллионов разрабов, но..

  • Не умеет в реактивность.

  • Ререндеры по любому чиху.

  • Смешивает инициализацию и обновление, логику и шаблон.

  • Путается между пересозданиями и перемещением.

  • Все компоненты либо неуправляемые, либо неполноценные, либо ожиревшие.

  • Кривая эмуляция объектов через функции с хуками.

  • Не типизируемый VDOM на выходе.

  • Разобщённая экосистема со слабой поддержкой TS.

  • Горы бойлерплейта по мере приближения к проду.

В продолжение темы: Реактивный React, Читерские бенчмарки.

Копилка благодарностейhttps://boosty.to/hyoo

Теги:
Всего голосов 12: ↑8 и ↓4+5
Комментарии6

Шаблон декомпозиции ModelView Fractal

Каждый ModelView выступает в роли модели/контроллера для ведомых ModelView и в качестве отображения для владеющего ModelView. Часть логики может выноситься как в чистые Model, так и в чистые View, которые являются лишь вырожденными случаями ModelView.

$my_user_list $my_view
	- \Owner ModelView
	users? /$my_user
	kids /
		<= Row*0 $my_user_row
			user <= user*

$my_user_row $my_card
	- \Having ModevView
	user $my_user
		avatar => image
		nickname => message

$my_card $my_view
	- \View not Model
	kids /
		<= Image $my_image
			uri <= image \about:blank
		<= Message $my_text
			text <= message \
	
$my_user $my_model
	- \Model not View
	avatar? \
	nickname? \

✅ Каждый ModelView полностью контролирует внутренние ModelView и ничего не знает про внешние.
✅ Любой ModelView может шариться между разными другими ModelView на любом уровне композиции.
✅ Изменение интерфейса ModelView требует изменения только лишь его владельцев.
✅ Фрактальная структура легко масштабируется на приложения любого размера.

Теги:
Всего голосов 7: ↑4 и ↓3+1
Комментарии0

Шаблон декомпозиции Model-View-Presenter

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

MVP
MVP
// Presenter
class User_preview {
	user: User
	card = new Card({
		image: ()=> this.user.avatar,
		message: ()=> this.user.nickname,
		color: ()=> this.user.skin.color,
		click: ()=> this.skin_change(),
	})
	skin_change() {
		this.user.skin = Skin.random()
	}
}

// View
<div class="Card" onclick={click} style={{ background: color }}>
	<img src={ image } />
	<p>{ message }</p>
</div>

// Model
class User extends Model {
	avatar: string
	nickname: string
	skin: Skin
}

✅ Легко добавлять новые отображения, не меняя модели. И наоборот.
✅ Изменение интерфесов модели или отображения требует изменения только лишь презентеров.
❌ Трёх слоёв слишком мало на больших масштабах.
❌ Для использования состояния одного презентера из другого необходимо искусственное вынесение его в модели.

Теги:
Всего голосов 10: ↑7 и ↓3+4
Комментарии0

Ближайшие события

Шаблон декомпозиции Model-View-Controller

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

MVC
MVC
// Controller
class Users_resource {
	GET() {
		return User.all.map( user_brief )
	}
}

// View
function user_brief( user: User ) {
	return {
		id: user.guid,
		name: user.passport.name_full,
	}
}

// Model
class User {
	
	static all = [] as User[]
	
	guid: GUID
	passports: Passport[]
	resumes: Resume[]
	
	get passport() {
		return this.passports[0]
	}
	
}

✅ Отображение может использовать произвольные модели с тем же интерфейсом.
✅ Легко добавлять новые отображения, не меняя модели. И наоборот.
❌ Для отображения разных типов моделей необходимо дублировать код отображения.
❌ Изменение интерфейса модели требует обновления всех использующих её отображений и контроллеров.
❌ Трёх слоёв слишком мало на больших масштабах.

Теги:
Всего голосов 8: ↑2 и ↓6-4
Комментарии7

Шаблон декомпозиции Model-View-ViewModel

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

MVVM
MVVM
// View
<li class="User_card" model="User_card_model">
	<img src={ image } />
	<p>{ message }</p>
</li>

// ViewModel
class User_card_model {
	user = User.current
	get image() {
		return this.user.avatar
	}
	get message() {
		return this.user.nickname
	}
}

// Model
class User {
	avatar: string
	nickname: string
	static current = new User
}

✅ Отображение может использовать произвольные вьюмодели.
✅ Легко добавлять новые отображения, не меняя ни модели, ни вьюмодели.
✅ Изменение интерфейса модели или отображения требует изменения только лишь вьюмодели.
✅ Одну и ту же вьюмодель можно шарить между несколькими отображениями.
❌ Для отображения разных моделей необходимо дублировать код отображения и вьюмодели.
❌ Трёх слоёв слишком мало на больших масштабах.

Теги:
Всего голосов 9: ↑6 и ↓3+3
Комментарии0

Шаблон декомпозиции View-Model

Код работы с моделями пишется прямо в отображении.

// View
function Task_list() {
	return <ul>{
		Task.list.map( task =>
			<li><Task_row {task} /></li>
		)
	}</ul>
}

// Model
class Task {
	static list = [] as Task[]
}

✅ Отображение может использовать произвольные модели.
✅ Легко добавлять новые отображения, не меняя модели.
❌ Для отображения разных моделей необходимо дублировать код отображения.
❌ Изменение интерфейса модели требует обновления всех использующих её отображений.
❌ Двух слоёв слишком мало на больших масштабах.

Теги:
Всего голосов 8: ↑4 и ↓40
Комментарии0

Шаблон декомпозиции Model-View

Модель знает как себя по разному представлять.

class User { // Model
	
	_id: bigint
	_nickname: string
	
	toString() { // View
		return 'user=' + this._id
	}
	
	toJSON() { // View
		return {
			id: String( this._id ),
			name: this._nickname,
		}
	}
	
}

✅ Удобно из модели получать любые отображения.
❌ Добавление нового отображения требует изменения модели.
❌ Отображение полностью определяется одной основной моделью.
❌ Загрузка модели вытягивает по зависимостям и все её отображения.
❌ Двух слоёв слишком мало на больших масштабах.

Теги:
Всего голосов 10: ↑6 и ↓4+2
Комментарии2

Препарируем Feature Slices Design и находим родовые травмы
https://youtu.be/tNx05dfFHRU

Стандартизированная декомпозиция, но..

  • Распыление каждой бизнес-фичи по всему проекту

  • Нечёткие сомнительные правила

  • Тонны бойерплейта на синглтонах

  • Ограниченная масштабируемость и гибкость

Поблагодарить, Обсудить.

Теги:
Всего голосов 5: ↑3 и ↓2+1
Комментарии0

Как правильно “придираться” во время код ревью

Когда вы проверяете код своего коллеги не нужно требовать от него “идеального” кода. 

ℹ️ Правило такое: вам следует апрувить изменения, если они улучшают в целом кодовую базу, даже если они не идеальны, ведь идеального кода не существует. Код можно сделать лучше чем он был до этого, но не идеальным.

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

ℹ️ В стандартах код ревью гугла, описано, что в таких ситуациях можно использовать префикс NIT, в комментариях к пул реквесту. NIT — сокращение от “nitpick” или “придираться”. 

❓В каких ситуациях стоит использовать NIT

- Чтобы выразить своё мнение или личное предпочтение. Очень важно отличать такие комментарии, от фактов. Довольно часто ревьюеры выставляют собственное мнение как истину и единственно верное решение

- Чтобы указать автору на мелкие замечания, которые не обязательно исправлять

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

Подробнее об этом можно почитать здесь:

- The Standard of Code Review


https://t.me/cherkashindev/112

Всего голосов 2: ↑1 и ↓10
Комментарии0