Яхья Картоев | Backend простым языком
Kanalga Telegram’da o‘tish
204
Obunachilar
Ma'lumot yo'q24 soatlar
Ma'lumot yo'q7 kun
Ma'lumot yo'q30 kun
Ma'lumot yuklanmoqda...
O'xshash kanallar
Ma'lumot yo'q
Muammo bormi? Iltimos, sahifani yangilang yoki bizning qo'llab-quvvatlash boshqaruvchimizga murojaat qiling>.
Taglar buluti
Ma'lumot yo'q
Muammo bormi? Iltimos, sahifani yangilang yoki bizning qo'llab-quvvatlash boshqaruvchimizga murojaat qiling>.
Kirish va chiqish esdaliklari
---
---
---
---
---
---
Obunachilarni jalb qilish
Yanvar '24
Yanvar '24
+204
0 kanalda
Kanal postlari
Объявляем duration правильно
❌ Плохо:
const someDuration = 720 * time.Hour // 1 month duration
✅ Хорошо:
const someDuration = 30 * 24 * time.Hour // 1 month duration
В первом случае хоть и стоит комментарий, но:
- на него сходу не падает глаз и ты первые доли секунды не понимаешь, что значит магическое число 720
- если нужно будет сократить количество дней, например до недели, придется считать количество часов в неделе
Во втором случае:
- когда видишь 24 * time.Hour сходу понимаешь, что речь о дне (странно, что нет time.Day), а дальше уже понимаешь, сколько дней. Комментарий дополняет на случай особой непонятливости
- если нужно изменить количество дней - просто меняешь первую цифру на необходимую| 2 | Уточнение по предыдущему посту.
То, о чем я писал, называется Type Definition.
В go 1.19 же оказывается появились Type Alias. При инициализации они отличаются только знаком равно: type UserId = int64. Если рассматривать проблемы, описанные ранее, Type Alias решают только первую - повышение прозрачности кода. Но перепутать аргументы с ними по-прежнему легко, что делает их куда более узко применимыми. Подробнее о различиях тут.
Теперь непонятно, как по-русски называть Type Definition, элиасом уже как-то не назовешь. Кастомный тип? Ваши варианты?
______________________________
@Backend простым языком | 254 |
| 3 | Короткий совет вместо длинного поста:
Создавайте элиасы на айдишники в golang. Как минимум вам самим же легче будет разобраться, где какой айдишник используется и что же это за ключ int64 в мапе.
Еще пример, когда это избавит от потенциальных багов. Имеем функцию
func getLesson(userId int64, lessonId int64) (Lesson, error)
При ее вызове легко ошибиться и перепутать местами аргументы. Компилятор на такое не пожалуется, так как оба типа аргументов одинаковые.
Как можно было сделать лучше? Использовать элиасы:
func getLesson(userId UserId, lessonId LessonId) (Lesson, error)
При такой декларации перепутать аргументы уже куда сложнее 🙂
______________________________
@Backend простым языком | 345 |
| 4 | О мусульмане, дуа за нашу Гaззy, дуа за мусульман! О Аллaх, пусть зeмля прoвалится под нoгaми этиx прoклятыx яхудов! Амин
📢📢📢 | 298 |
| 5 | Сегодня Израиль (оккупированная территория Палестины) проводит невиданный миру за последние 50 лет геноцид палестницев, и весь "современный" мир закрывает на это глаза! 🇵🇸
Израильские террористы наносят авиаудары по больницам, где находятся больные дети, женщины, старики и через час заявляют, что это сделали сами палестинцы!
Людям навязывается диктат покинуть свои дома и в то же время наносятся авиаудары по мирной колонне!
Израильтяне оставили сегодня палестинцев без воды, света, интернета [1] [2] и медикаментов! При этом люди оказались в полной изоляции, без возможности бежать в соседние страны.
Я понимаю, что это канал про IT и как вы могли заметить, я никогда ранее не затрагивал политические темы, но речь сейчас идёт не о политике, а о преступлении против человечества. И хотите отписывайтесь от меня, хотите нет, но не поделиться этим я не могу!
(на фото так называемые "террористы", слабонервным не смотреть ⚠️) | 391 |
| 6 | Какой язык программирования выбрать бэкендеру или почему я советую golang? Часть 2.
Golang как по мне отлично подходит по всем пунктам:
1. С деньгами проблем нет, за него платят как минимум не меньше, чем за конкрутентов. Количество вакансий по ощущениями только растет.
2. Язык изначально заточен под легкость освоения, эта цель стояла у истоков его создания. Он сильно ограничен по количеству ключевых слов, притом не в ущерб возможностям. Есть проблема, что в русскоязычном пространстве ты не найдешь тонны курсов на любой вкус, но это может быть и плюсом.
3. Go создан в 2009ом году, то есть является довольно молодым, притом прогрессивно развивающимся языком, за которым стоит IT гигант Google. В пользу его современности и в целом главным аргументом для меня является то, что крупные компании переходят на него с других языков. Я думаю не нужно быть техническим директором, чтобы понимать: если крупная компания принимает решение о выборе нового основного языка, это должно быть чем-то крайне обоснованным и перспективным, так как сулит большие затраты и риски. Список компаний, ныне использующих Golang можете найти в поисковике, но вот короткий список известных мне компаний, перешедших на него:
- Авито - с PHP на Golang
- Wildberries - с C# на Golang
- Goods (ныне МегаМаркет) - c Nodejs на Golang
Решать в конечном счете вам самим. Главное помнить, что язык это только инструмент, его можно сменить в любой момент, и не стоит думать, что это выбор на всю жизнь.
______________________________
@Backend простым языком | 419 |
| 7 | Какой язык программирования выбрать бэкендеру или почему я советую golang? Часть 1.
Осторожно: Статья супер субъективная. Когда ты пишешь на каком-то языке, создается ощущение, что весь мир крутится вокруг него, а остальные языки умирают (кроме php, там мир тебе напомнит, как обстоят дела), поэтому объективным быть в данном вопросе тяжело.
Теперь по поводу Golang. Предлагаю определиться с основными требованиями при выборе языка программирования. На мой взгляд от языка ожидается, чтобы:
1. Он приносил деньги - количество вакансий, уровень зарплат
2. Его относительно легко можно было освоить - обучающие материалы, курсы, отсутствие исторической нагроможденности языка ненужными возможностями
3. Был современным - тут тоже можно смотреть на вакансии, отсутствие жестких ограничений, использование крупными компаниями
______________________________
@Backend простым языком | 497 |
| 8 | Делюсь с вами крайне полезным инструментом, который судя по всему далеко не нов, но умудрялся обходить меня стороной. PlantUML - (грубо говоря) язык разметки, который позволяет текстом описывать разные диаграммы (sequence, activity, etc.) и генерить их представление с помощью плагинов. Особенно незаменимая вещь в крупных компаниях, где ценится документирование своей работы и в целом подобные артефакты.
Как начать пользоваться:
- Кидаете кусок кода в chatGPT с запросом generate me plantUML sequence diagram for code below
- Сохраняете результат к себе в проект и редактируете под нужды
- Profit
На сайте есть хорошая документация с множеством примеров, советую ознакомиться.
______________________________
@Backend простым языком | 546 |
| 9 | Tab-chaos.
Хочу поделиться подходом к работе с вкладками в редакторе кода, о котором я узнал много лет назад и с тех пор не представляю себе, как можно работать иначе.
Все очень просто: ограничьте в редакторе количество активных вкладок до одной. Как говорил Брайан Трейси: “Я обещаю, это изменит вашу жизнь”. Больше не придется постоянно контролировать этот таб-хаус, клацать “Close other tabs”, искать среди тысячи открытых ту самую. Одна активная вкладка в один момент времени. Все.
А как же тогда находить предыдущие вкладки?
Множество открытых вкладок не сильно помогает в решении этой проблемы. К предыдущим можно вернуться через cmd+tab, но еще лучше настроить хоткей на возврат к предыдущим/следующим положениям курсора - еще один маст хэв в работе.
______________________________
@Backend простым языком | 476 |
| 10 | Отложенный выстрел в ногу в SQL
Нашел в коде примерно следующий запрос:
SELECT books.id,
img_url,
name
FROM books
JOIN authors ON books.author_id = authors.id;
То есть получаем айдишники книги, их картинки и имена авторов, которые хранятся в другой таблице. Запрос рабочий, вроде нет проблем, да? До поры до времени.
Сегодня он рабочий, потому что колонка img_url есть только в таблице books, а колонка name только в таблице authors. А вот айдишник есть в обеих таблицах, поэтому мы были вынуждены прописать явно из какой таблицы его брать.
Завтра (или через год) коллеге прилетает задача, что у авторов тоже есть лицо и мы хотим сохранять в таблицу их картинки. Он смело идет и первым делом добавляет в таблицу authors колонку img_url, а далее раскатывает на прод, ведь что можем быть безобиднее, чем просто добавить никого не трогающую новую колонку.
Но не тут то было. В другом конце кода лежит заряженный SQL запрос, который только и ждет, когда спустят курок. С добавлением в таблицу authors колонки img_url, наш запрос выше сломается, так как теперь неясно, какая именно колонка img_url в нем подразумевается. По итогу запрос будет выдавать ошибку ERROR: column reference “img_url” is ambiguous
Мораль: если вы в запросе джойните таблицы, всегда в селекте указывайте, откуда брать каждую из колонок
Запрос на предохранителе👇
SELECT b.id,
b.img_url,
a.name
FROM books b
JOIN authors a ON b = a;
______________________________
@Backend простым языком | 398 |
| 11 | Хочешь добавить прогон юнит-тестов в гитлабе при создании мерж-реквеста и подозреваешь, что джоба не будет падать, если тесты не прошли
Раньше:
- Гуглишь, копаешься на стаковерфлоу, страдаешь - минимум минут 10 и больше
Сейчас:
- Спрашиваешь GPT, получаешь ответ - менее минуты (остальные 9 пишешь об этом пост в канал)
______________________________
@Backend простым языком | 284 |
| 12 | PostgreSQL timestamp.
TL;DR Всегда используйте для дат timestamp with time zone
Ситуация: В базе есть колонка updated_at, тип которой timestamp, а значение по умолчанию CURRENT_TIMESTAMP.
Проблема: Вычитываем значение этой колонки, сравниваем с time.Now(), и оказывается, что в колонке лежит время +3 часа по сравнению с тем, что дает гошный time. То есть в часовом поясе Москвы, притом без указания таймзоны, что лишает нас возможности перевести time в ту же часовую зону для корректного сравнения времен.
Неожиданно оказалось, что CURRENT_TIMESTAMP при конвертировании в timestamp сохраняет текущее время не в UTC, как можно было бы ожидать, а в часовом поясе клиента, который инициировал запрос (теряя при этом инфу о таймзоне). А если клиентский он не знает, то в часовом поясе сервера, на котором он находится.
Решение:
- Если у вас маленькая база, то накатить миграцию с изменением типа колонки
- Если база большая и не хочется мучаться с аккуратным переездом на новый тип, при сохранении даты явно указывать таймзону timezone('UTC', CURRENT_TIMESTAMP)
- В будущем всегда использовать timestamp with time zone
______________________________
@Backend простым языком | 631 |
| 13 | Нотации.
Краткий ликбез по нотациям - способам именование пременных, папок и тд - в программировании.
Самые популярные, их название и одновременно пример:
- camelCase
- snake_case
- PascalCase
- kebab-case
В Golang (да и везде) в коде обычно используется camelCase. Для наименования файлов и папок мы в команде используем snake_case.
В реляционной бд для названия таблиц и колонок обычно всегда используют snake_case. В любом другом регистре насколько я помню при запросах придется брать название таблицы/колонки в кавычки, а при snake_case в этом необходимости нет.
______________________________
@Backend простым языком | 312 |
| 14 | SQL-лайфхак.
Реальная задача с одного из проектов (немного упрощена для понимания):
Есть таблица с уроками пользователей, которые они проходят.
CREATE TABLE user_lessons(
user_id INTEGER NOT NULL,
lesson_id INTEGER NOT NULL
);
Необходимо было вычитать уникальные айдишники уроков, по которым нет записей у определенного пользователя, но есть записи у других. Например, при таких данных:
user_id | lesson_id
1233 | 12
2645 | 12
5823 | 13
5823 | 14
3123 | 18
Для пользователя 1233 запрос должен был вернуть 13, 14, 18, потому что эти уроки он не проходил.
Первое, что приходит на ум, это сделать подзапрос
SELECT DISTINCT lesson_id from user_lessons where lesson_id NOT IN (
SELECT DISTINCT lesson_id from user_lessons where user_id = 1233
)
Но во-первых, к подзапросам всегда было внутреннее отторжение, так как они тяжело читаются и меня не покидают сомнения в их производительности. А во-вторых, в реальном примере все это сопровождалось несколькими джойнами как в основном запросе, так и в подзапросе, что еще больше усугубляло перечисленное.
В процессе решения я все никак не мог вспомнить лайфхак для таких случаев. Точно помнил, что он есть и даже в чем его примерный смысл, но не хватало последнего элемента пазла. Что делать в таких случаях? Расчехлять GPT, на этот раз китайский - без регистрации, смс и VPN.
Первым делом он тоже предложил подзапрос, но услышав в ответ how about not using subquery?, выдал необходимое решение 🎯
Кидайте свой вариант решения без подзапроса в комменты или смотрите лайфхак ниже:
Лефтджойним эту же таблицу, только с указанием текущего пользователя и находим строки, у которых он не проставился. Профит.
SELECT DISTINCT lesson_id from user_lessons ul1
LEFT JOIN user_lessons ul2 on ul1.lesson_id = ul2.lesson_id AND ul1.user_id = 1233
WHERE ul2.user_id IS NULL
______________________________
@Backend простым языком | 495 |
| 15 | Аккуратнее с errgroup.
Есть очень популярная библиотека, которая позволяет удобно запускать несколько горутин, дожидаться их выполнения и получать первую возникшую ошибку. И у нее есть очень неприятные грабли, связанные с контекстом.
Обычный пример:
// здесь выше какой-то код, у которого есть контекст уровня запроса к серверу
…
// создаем errgroup с контекстом, чтобы связывать горутины между собой и при ошибке в одной из них, валить остальные, не дожидаясь их окончания
eg, ctx := errgroup.WithContext(ctx)
// запускаем несколько горутин
eg.Go(func() error {
var err error
images, err = getImages(ctx)
return err
})
eg.Go(func() error {
var err error
books, err = getBooks(ctx)
return err
})
// дожидаемся их выполнения
err := eg.Wait()
if err != nil {
return err
}
…
// идем дальше использовать контекст
Кто скажет, что не так с этим кодом?
Проблема в том, что мы перетерли контекст запроса контекстом, который предназначается для горутин. На первый взгляд это может показаться не проблемой, но если зайти внутрь метод eg.Wait мы увидим следующий код:
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel()
}
return g.err
}
То есть контекст errgroup отменяется в любом случае, даже если ошибки не произошло. И правильным при инициализации errgroup с контекстом будет создать отдельную переменную под контекст группы и внутри горутин использовать ее, не затрагивая общий контекст:
eg, egCtx := errgroup.WithContext(ctx)
Натыкались на эти грабли?
______________________________
@Backend простым языком | 547 |
| 16 | Профили инженеров
В одном из прошлых постов мы говорили об уровнях программистов и проблемах классического подхода с 3 уровнями. На текущем же месте работы в определенный момент ввели куда более гибкую и прозрачную систему оценки.
Первой проблемой классического подхода я озвучивал недостаточное количество уровней для такой сложной профессии. По новой системе уровней целых 8 (от E1 до E8). Если проводить аналогию, инженеров Е1-Е2 можно отнести к джунам, Е3-Е4 к миддлам, Е5-Е6 это синьйоры, а выше уже некие эксперты.
Остается вторая проблема - определение уровня. Для этого в компании выделили 7 направлений оценки и требования по этим направлениям, которые ожидают от программиста на каждом из уровней.
Для примера часть требований к уровню Е4:
- Экспертность
- Декомпозирует задачи. Умеет оценивать технические риски. Предлагает меры по их смягчению или устранению.
- Работает с неопределённостью, например открытыми задачами в своей области ответственности. Понимает, что нужно сделать, но может не понимать, как.
- Инженерная культура
- Даёт содержательные комментарии на Code Review.
- Находит баланс между скоростью и качеством разработки/тестирования.
- Ориентация на бизнес
- Убеждается, что цели команды прокачивают продукт и позитивно влияют на пользователей. Помогает формировать бэклог, исходя из ценностей компании.
- Помогает владельцу продукта декомпозировать крупные фичи на набор полезных инкрементов, которые можно релизить независимо.
Конечно при желании можно придраться моментами к общности формулировок. Плюс не всегда понятно, как прийти к тому, чтобы соответствовать некоторым требованиям. Но это не жесткий список, а скорее ориентир как для программиста, который теперь видит, чего от него ждут на каждом из уровней и куда стоит двигаться, так и для менеджмента, которому есть на что опираться при принятии решения о повышении.
С полным списком требований для всех уровней можно ознакомиться по ссылке.
Как вам такая система оценки?
______________________________
@Backend простым языком | 574 |
