TypeScript делает код надежным, но неправильное использование приводит к багам. Следуйте лучшим практикам!
any отключает проверку типов — это как @ts-ignore для всей переменной. Опасно!
// ❌ Плохо
function process(data: any) {
return data.someMethod(); // Никаких проверок!
}
// ✅ Хорошо
function process(data: unknown) {
if (typeof data === 'object' && data !== null) {
// Безопасная проверка перед использованием
}
}
Правило: unknown заставляет вас явно проверить тип перед использованием. Это безопаснее!
// ❌ Плохо
let str: String = new String("hello");
let num: Number = new Number(42);
let bool: Boolean = new Boolean(true);
// ✅ Хорошо
let str: string = "hello";
let num: number = 42;
let bool: boolean = true;
Используйте примитивные типы: string, number, boolean, bigint, symbol.
Обычные enum генерируют дополнительный код во время выполнения.
const enum встраивает значения напрямую!// ❌ Генерирует runtime код
enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE"
}
// ✅ Встраивается при компиляции
const enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE"
}
Результат: Меньше кода, быстрее выполнение!
TypeScript поддерживает опциональную цепочку (
?.) и нулевое слияние (??):// ❌ Старый способ
const name = user && user.profile && user.profile.name || 'Guest';
// ✅ Современный способ
const name = user?.profile?.name ?? 'Guest';
Преимущества:
TypeScript предоставляет мощные инструменты для преобразования типов:
interface User {
id: number;
name: string;
email: string;
password: string;
}
// Все поля опциональны
type PartialUser = Partial<User>;
// Все поля только для чтения
type ReadonlyUser = Readonly<User>;
// Выбрать определенные поля
type PublicUser = Pick<User, 'id' | 'name' | 'email'>;
// Исключить определенные поля
type UserWithoutPassword = Omit<User, 'password'>;
// Сделать определенные поля обязательными
type RequiredName = Required<Pick<User, 'name'>>;Комбинируйте их:
type SafeUser = Readonly<Omit<User, 'password'>>;
Итог
TypeScript — мощный инструмент, но важно использовать его правильно:
Следуя этим рекомендациям, вы пишете не просто типизированный код, а надежный и поддерживаемый TypeScript!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Ключевое слово, которое часто упускают из виду, но оно критически важно для элегантного кода!
Что такое mutable?
Позволяет изменять член данных константного объекта, не нарушая его логическую неизменяемость.
class Example {
public:
void setValue(int val) const {
value = val; // Работает благодаря mutable!
}
private:
mutable int value;
};Четыре основных сценария использования:
Сохраняйте результаты дорогостоящих вычислений:
class DataProcessor {
public:
int getSum() const {
if (!cachedSum_) {
cachedSum_ = std::accumulate(data_.begin(),
data_.end(), 0);
}
return *cachedSum_;
}
private:
std::vector<int> data_;
mutable std::optional<int> cachedSum_;
};Преимущество: Вычисляем один раз, используем многократно!
class LazyString {
public:
const std::string& getUpperCase() const {
if (!upperCase_) {
upperCase_ = original_;
std::transform(upperCase_->begin(),
upperCase_->end(),
upperCase_->begin(),
::toupper);
}
return *upperCase_;
}
private:
std::string original_;
mutable std::optional<std::string> upperCase_;
};Экономия: Вычисляем только когда действительно нужно!
class ThreadSafeCounter {
public:
int getValue() const {
std::lock_guard<std::mutex> lock(mutex_);
return value_;
}
private:
int value_ = 0;
mutable std::mutex mutex_; // Можем блокировать в const методах
};class BigData {
public:
int get(size_t index) const {
++accessCount_; // Не меняет логическое состояние
return data_[index];
}
size_t getAccessCount() const {
return accessCount_;
}
private:
std::vector<int> data_;
mutable size_t accessCount_ = 0;
};class Configuration {
public:
Configuration(const std::string& filename)
: filename_(filename) {}
std::string getValue(const std::string& key) const {
loadConfigIfNeeded(); // Загружаем только при первом обращении
return config_[key];
}
private:
void loadConfigIfNeeded() const {
if (!isLoaded_) {
// Загружаем конфигурацию из файла
isLoaded_ = true;
}
}
std::string filename_;
mutable bool isLoaded_ = false;
mutable std::unordered_map<std::string, std::string> config_;
};Использование:
const Configuration config("settings.conf");
// Файл загрузится только здесь ↓
std::cout << config.getValue("log_level");1. Потокобезопасность
// ❌ НЕ потокобезопасно!
mutable int count_ = 0;
void increment() const { ++count_; }
// ✅ Потокобезопасно
mutable int count_ = 0;
mutable std::mutex mutex_;
void increment() const {
std::lock_guard<std::mutex> lock(mutex_);
++count_;
}
2. Не нарушайте логическую неизменяемость
// ❌ Плохо - меняет логическое состояние
class Bad {
void setData(int d) const { data_ = d; }
mutable int data_;
};
// ✅ Хорошо - только внутренняя оптимизация
class Good {
int getData() const {
++accessCount_; // Метаданные, не логика
return data_;
}
int data_;
mutable size_t accessCount_ = 0;
};
3. Документируйте использование
class Example {
private:
// mutable используется для кэширования
// с целью повышения производительности
mutable std::optional<int> cachedValue_;
};Итог
mutable — мощный инструмент для:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Обрабатываете миллионы записей и приложение тормозит? 11 проверенных техник оптимизации Java Spring для работы с большими данными!
Проблема:
Приложение должно обработать 10 000+ транзакций по каждому счету. Как не упасть в OutOfMemoryError?
// ❌ Плохо
for (int i = 0; i < list.size(); i++)
// ✅ Хорошо
int size = list.size();
for (int i = 0; i < size; i++)
Экономия: вызов size() выносится за цикл
Делим данные на батчи по 100-1000 записей
for (int i = 0; i < size; i += 100) {
List<T> batch = data.subList(i, min(i+100, size));
repository.saveAll(batch);
repository.flush(); // освобождаем память
}// ❌ Создает промежуточные коллекции
.collect(Collectors.toList())
// ✅ Прямая обработка
.forEach(System.out::println)
• ArrayList вместо LinkedList (меньше памяти)
• int вместо Integer (без автобоксинга)
• HashMap с начальной емкостью
// Используем все ядра процессора
list.parallelStream().forEach(this::process);
// ❌ В цикле
new String("Result")
// ✅ Переиспользование
String result = "Result";
Не используйте Optional в полях класса — это лишние объекты-обертки!
java -Xms1g -Xmx2g -XX:+UseG1GC \
-XX:MaxGCPauseMillis=200
Включите на проде для мониторинга:
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
// Предотвращаем утечки памяти
WeakHashMap<String, T> cache = new WeakHashMap<>();
Не сериализуйте лишние поля:
private transient int salary;
Банковское приложение обрабатывало остатки по счетам с 10К+ транзакций. После применения этих техник:
Частые ошибки:
Измеряйте производительность! Используйте профайлеры (JProfiler, VisualVM) перед оптимизацией.
Чек-лист перед продом
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Строки в JS — это не просто текст, а швейцарский нож для разработчика! Вот 10 приемов, которые сделают ваш код чище и быстрее.
const reverse = str => str.split('').reverse().join('');
reverse("hello"); // "olleh""hello world".includes("world"); // trueconst toTitleCase = str => str.split(' ')
.map(word => word[0].toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
toTitleCase("javascript tricks");
// "Javascript Tricks"" hello world ".trim();
// "hello world"
"hello ".repeat(3);
// "hello hello hello "
const count = (str, sub) => str.split(sub).length - 1;
count("hello world, hello JS", "hello"); // 2
"42".padStart(5, "0"); // "00042"
"7".padEnd(3, "0"); // "700"
"banana".replaceAll("a", "o"); // "bonono"
// Или через regex:
"banana".replace(/a/g, "o");const onlyLetters = str => str.replace(/[^a-zA-Z]/g, '');
onlyLetters("a1b2c3"); // "abc"
[..."hello"];
// ['h', 'e', 'l', 'l', 'o']
// Подсчет уникальных символов
const uniqueChars = str => [...new Set(str)].length;
uniqueChars("hello"); // 4
// Удаление дубликатов
const removeDupes = str => [...new Set(str)].join('');
removeDupes("hello"); // "helo"
Почему это важно
Перед тем как писать свою функцию для работы со строками, проверьте — возможно, встроенный метод уже решает вашу задачу!
Частые ошибки:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
В C++ нет нативной поддержки switch для строк, но есть 4 мощных способа это обойти! Разбираем от простого к продвинутому.
Проблема:
// ❌ Так нельзя в C++
switch(str) {
case "hello": ...
case "quit": ...
}
Самый простой и быстрый способ:
enum class StringCode {
hello, quit, help, unknown
};
StringCode hashString(const string& str) {
if (str == "hello") return StringCode::hello;
if (str == "quit") return StringCode::quit;
if (str == "help") return StringCode::help;
return StringCode::unknown;
}
switch (hashString(command)) {
case StringCode::hello:
cout << "Hello there!" << endl;
break;
case StringCode::quit:
cout << "Goodbye!" << endl;
break;
}Для динамической обработки:
class CommandProcessor {
unordered_map<string, function<void()>> commands;
public:
CommandProcessor() {
commands["help"] = []() {
cout << "Help info" << endl;
};
commands["add"] = []() {
cout << "Adding..." << endl;
};
}
void execute(const string& cmd) {
auto it = commands.find(cmd);
if (it != commands.end()) {
it->second();
}
}
};Хеширование на этапе компиляции:
constexpr uint64_t hash(string_view str) {
uint64_t hash = 0;
for (char c : str) {
hash = (hash * 131) + c;
}
return hash;
}
constexpr uint64_t operator""_hash(const char* str, size_t len) {
return hash(string_view(str, len));
}
switch (hash(action)) {
case "save"_hash:
cout << "Saving..." << endl;
break;
case "load"_hash:
cout << "Loading..." << endl;
break;
}Для высоконагруженных систем:
class StringPool {
unordered_set<string> pool;
public:
const string* intern(const string& str) {
auto [it, inserted] = pool.insert(str);
return &(*it);
}
};
// Сравниваем указатели, а не строки!
if (str == pool.intern("start")) {
// быстрее обычного сравнения строк
}class TextEditor {
enum class CommandType {
insert, delete_line, move, print, unknown
};
CommandType getCommandType(const string& cmd) {
if (cmd == "insert" || cmd == "i")
return CommandType::insert;
if (cmd == "delete" || cmd == "d")
return CommandType::delete_line;
// ...
}
void executeCommand(const string& cmdLine) {
switch (getCommandType(cmd)) {
case CommandType::insert: /* ... */ break;
case CommandType::delete_line: /* ... */ break;
}
}
};Сравнение производительности:
Выбор метода:
Небольшой фикс. набор строк → Enum hash
Динамические команды → unordered_map
Критична скорость → Constexpr hash
Много повторений → String Pool
Важные моменты
Pro-tip:
Комбинируйте подходы! Например:
Реальные кейсы применения:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Пишете код без тестов? Готовьтесь часами искать баги! Разбираем, как unittest спасет ваше время и нервы.
Простыми словами: это проверка, что ваш код работает правильно. Большинство делает это через print() и запуск в терминале. Но есть способ лучше!
Вместо часов отладки — мгновенное обнаружение проблем
Заставляет продумывать входы, выходы и граничные случаи
Копируете функцию? Копируйте и тесты!
Можно даже писать тесты ДО написания кода (test-driven development)
Типы тестов:
Проверка отдельных функций/компонентов
Проверка взаимодействия всех частей
Сегодня фокус на модульном!
def add(x, y):
return x + y
# Базовый тест
assert add(2, 4) == 6, "Should be 6"
Если результат не 6 → получите AssertionError
Unittest: профессиональный подход
Встроенный в Python тестировщик. Без установки пакетов!
Структура проекта:
project/
├── functions.py
└── tests.py
functions.py:
def add(x, y):
return x + y
tests.py:
import unittest
from functions import add
class TestAdd(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 4), 6)
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(-1, -1), -2)
if __name__ == '__main__':
unittest.main()
Запуск:
python tests.py
Результат:
...
-------------------------------
Ran 1 test in 0.001s
OK
Каждая точка (.) = успешный тест
E = ошибка
F = провал теста
# ✅ Работает
def test_add():
pass
# ❌ НЕ работает (не начинается с test_)
def add_test():
pass
Название ДОЛЖНО начинаться с test_!
Сначала очевидные случаи:
def test_add_positive():
self.assertEqual(add(2, 3), 5)
Потом граничные:
def test_add_zero():
self.assertEqual(add(0, 5), 5)
self.assertEqual(add(5, 0), 5)
Что может сломать ваш код?
def test_edge_cases():
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(0, 0), 0)
self.assertEqual(add(-5, -3), -8)
Каждый тест — отдельная вселенная!
class TestCalculator(unittest.TestCase):
def setUp(self):
# Выполняется ПЕРЕД каждым тестом
self.calc = Calculator()
def tearDown(self):
# Выполняется ПОСЛЕ каждого теста
self.calc = None
self.assertTrue(add(2, 3) == 5)
# Вывод: Expected True, but was False
self.assertEqual(add(2, 3), 5)
# Вывод: AssertionError: 5 != 6
Второй вариант даёт НАМНОГО больше информации!
def round_plus_ten(x):
return round(x) + 10
class TestRound(unittest.TestCase):
def test_round(self):
self.assertEqual(round_plus_ten(4.3), 14) # ✅
self.assertEqual(round_plus_ten(4.7), 15) # ✅
self.assertEqual(round_plus_ten(4.5), 15) # ❌ FAIL!
Почему упало?
round(4.5) = 4, а не 5! Python округляет к ближайшему чётному.
Группировка тестов:
class TestAddition(unittest.TestCase):
def test_positive(self): ...
def test_negative(self): ...
class TestMultiplication(unittest.TestCase):
def test_basic(self): ...
Пропуск тестов:
@unittest.skip("Пока не работает")
def test_future_feature(self):
passПроверка исключений:
def test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
divide(10, 0)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Best Practices:
✅ Один тест = одна проверка
✅ Тесты должны быть быстрыми (<1 сек)
✅ Понятные названия: test_add_negative_numbers
✅ Не тестируйте сторонние библиотеки
✅ Покрытие кода 70-80% — отличный результат
✅ Коммитьте тесты вместе с кодом
❌ Не делайте тесты зависимыми друг от друга
❌ Не используйте случайные данные без seed
❌ Не игнорируйте упавшие тесты
🔸 Workflow разработки:
1. Пишете функцию
2. Сразу пишете тест
3. Запускаете тест
4. Если упал → чините код
5. Повторяете до зелёных тестов
6. Добавляете edge cases
7. Профит!💫
🔸 Альтернативы unittest:
🔸 pytest — более простой синтаксис
🔸 nose2 — расширение unittest
🔸 doctest — тесты в docstrings
Но unittest — это стандарт Python!
💡 Золотое правило:
«Если нашли баг — напишите тест, который его воспроизводит. Потом чините код. Так этот баг никогда не вернётся!»
Реальная статистика⬇️
🔸 Проекты с тестами имеют на 40% меньше багов
🔸 Рефакторинг с тестами = безопасность
🔸 Тесты = документация вашего кода
🔸 Код с тестами легче поддерживать
📎 Статья
🎙 Новости
📝 База вопросов
1. Пишете функцию
2. Сразу пишете тест
3. Запускаете тест
4. Если упал → чините код
5. Повторяете до зелёных тестов
6. Добавляете edge cases
7. Профит!
Но unittest — это стандарт Python!
«Если нашли баг — напишите тест, который его воспроизводит. Потом чините код. Так этот баг никогда не вернётся!»
Реальная статистика
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Забываете отмечать рабочее время? Делаем простой бот на Node.js + TypeScript!
Жене нужен был отчёт за 3 месяца работы, но инструмента для учёта времени не было. Сначала писала "начало"/"конец" в WhatsApp группу. Решил автоматизировать это через Telegram Bot!
• Node.js + TypeScript
• node-telegram-bot-api
• CSV для хранения
• Docker для деплоя
npm i --save node-telegram-bot-api
npm i --save @types/node-telegram-bot-api
Идём к @BotFather → /newbot → получаем токен
import TelegramBot from 'node-telegram-bot-api';
const token = 'YOUR_TOKEN';
const bot = new TelegramBot(token, { polling: true });
// Начать задачу
bot.onText(/\/work (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const work = match[1];
const startDate = new Date();
const entries = await getEntries();
entries.push({
work,
startDate: startDate.toJSON(),
endDate: null,
duration: null,
});
writeFile(entries);
bot.sendMessage(chatId,
`Started ${work} at ${startDate.toLocaleString()}`);
});
// Завершить задачу
bot.onText(/\/done(.*)/, async (msg) => {
const chatId = msg.chat.id;
const endDate = new Date();
const entries = await getEntries();
const targetEntry = entries[entries.length - 1];
targetEntry.endDate = endDate.toJSON();
const startDate = new Date(targetEntry.startDate);
const diff = endDate - startDate;
targetEntry.duration = formatDuration(diff);
writeFile(entries);
bot.sendMessage(chatId,
`Finished ${targetEntry.work}. Took ${diff}!`);
});
const getEntries = async () => {
const val = fs.readFileSync(csvFilePath).toString();
const entries = [];
val.split("\n").forEach((row, index) => {
if (row === "" || index == 0) return;
const [work, startDate, endDate, duration] =
row.split(';');
entries.push({ work, startDate, endDate, duration });
});
return entries;
}
const writeFile = (entries) => {
let str = "work;startDate;endDate;duration;\n";
entries.forEach(e => {
str += Object.keys(e).map(k => e[k]).join(';') + '\n';
});
fs.writeFileSync(csvFilePath, str);
};/work Написать статью
→ Started at 9:00 AM
/done
→ Finished. Took 3h 25m!
→ work;startDate;endDate;duration;
Статья;2025-01-20T09:00...;3h 25m
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install && npm run build
CMD ["node", "build/main/index.js"]
docker build -t timetracker .
docker run -ti timetracker
Не коммитьте токен! Используйте
.env:import dotenv from 'dotenv';
dotenv.config();
const token = process.env.TELEGRAM_BOT_TOKEN;
Итог
За один вечер — полнофункциональный тайм-трекер! ~200 строк кода, работает везде, данные в удобном формате.
Please open Telegram to view this post
VIEW IN TELEGRAM
Изолированная среда для запуска приложений!
Изолированная «песочница», где процессы работают отдельно от ОС.
Внутри: все зависимости, библиотеки, конфигурации.
Результат: запускаешь везде — ничего устанавливать не нужно.
Правило: 1 контейнер = 1 процесс
Цепочка:
Dockerfile → Image (шаблон) → Container (экземпляр)
Аналогия: Image = класс, Container = объект
ВМ: гипервизор + полная ОС + много ресурсов
Контейнер: только нужное + разделяемая ОС + минимум ресурсов
• Микросервисы
• CI/CD
• Масштабирование
• Разработка ("Works on my machine" решено)
• Облако (AWS, Google, Azure)
docker pull node:18-alpine
docker run -p 3000:3000 my-app
docker stop container_id
Без: установка → зависимости → настройка → молитва
С:
docker run my-appИтог
Контейнеры: легкие, быстрые, портативные, эффективные, изолированные.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ускорение API в 300 раз!
Без кэша: 3000 мс
С Redis: 10 мс
Зависимости:
spring-boot-starter-data-redis + jedis
Конфигурация:
RedisTemplate + CacheManager + @EnableCaching
@Cacheable — кэширует результат (GET)
@CacheEvict — очищает кэш (POST/PUT/DELETE)
@CachePut — обновляет кэш
@Caching — группирует операции
@CacheConfig — настройки класса
1-й запрос: Redis пусто → БД → сохраняем в кэш → 3000 мс
2-й запрос: Redis → 10 мс
Изменение: @CacheEvict очищает кэш
Cache-Aside: @Cacheable (популярная)
Write-Through: @CachePut (актуальные данные)
Invalidation: @CacheEvict (сложная)
Thundering Herd: все в БД → cache warming
Memory Limits: переполнение → eviction policies
Serialization: объекты Serializable
Без кэша: 100 запросов = 300 сек, нагрузка 100%
С кэшем: 100 запросов = 4 сек, нагрузка 1%
Ускорение в 75 раз!
Итог
5 аннотаций + простая настройка = прирост в 75-300 раз. Must-have для production.
Please open Telegram to view this post
VIEW IN TELEGRAM
Разбираем самый важный цикл в программировании!
Цикл с предусловием. Логика: "ПОКА условие верно — ДЕЛАЙ это"
Запрашиваем данные, пока не получим корректные
Читаем, пока есть данные (размер заранее неизвестен)
while(true) → показываем меню → выполняем → "Выход" = break
Игра продолжается, пока не gameOver или не победа
Собираем данные, пока не получим сигнал остановки
While: проверка ДО → может не выполниться
Do-While: проверка ПОСЛЕ → минимум 1 раз
Итог
While — основа C++: гибкий, универсальный, для ввода, файлов, меню, игр и данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from YeaHub
В этом видео мы подробно разбираем профессию Android-разработчик: кто это, чем занимается, какие навыки нужны и сколько можно зарабатывать. Наш гость — сеньор Android-разработчик с опытом работы в Яндекс, МТС и Wildberries, который делится личным опытом: как пройти путь от новичка до ведущего специалиста всего за 5 лет.
Вы узнаете:
— Что делает Android-разработчик и как выглядит его рабочий день
— Какие качества и навыки нужны для успешного старта
— Сколько времени потребуется на обучение и с чего лучше начинать
— Какие инструменты и технологии нужно знать
— Топ-3 ресурса для старта Android-разработки
— Советы для первого проекта и развития карьеры
Если вы хотите стать Android-разработчиком или просто интересуетесь IT-профессиями, это видео точно для вас!
Подпишитесь на канал, чтобы не пропустить новые видео про карьеру в IT и обучение программированию.
https://yeahub.ru/ - тренажер для подготовки к собеседованиям
https://t.me/yeahub_android_mobile - канал YeaHub в Android
https://t.me/gulyaev_it - телеграмм-канал Антона об Android и IT
https://www.youtube.com/@gulyaev_it - ютуб-канал Антона
#it #android #kotlin #программирование #разработка #собеседование #трудоустройство
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2🔥1
Работают, но непонятно где применять. Разбираем 6 необычных свойств!
Прячет пустые ячейки в таблицах.
empty-cells: hide
Выравнивает только последнюю строку текста.
Текст обтекает фигуры (круг, эллипс, многоугольник), а не квадрат.
shape-outside: circle(50%)
Меняет дефис при переносе на любой символ.
hyphenate-character: "="
Настраивает ширину табуляции в коде.
tab-size: 1rem
Добавляет символы НАД текстом для ударений.
text-emphasis: sesame
Встречали эти свойства в реальных проектах? Или они правда странные?
Please open Telegram to view this post
VIEW IN TELEGRAM
Упрощаем работу с React через кастомные хуки.
Задача: выполнить код только при первом рендере
Проблема:
useEffect(() => {}, []) — непонятно, что выполняется один разРешение:
useMount(() => {}) — явная семантикаЗадача: выполнить код при размонтировании
Проблема:
useEffect(() => { return () => {} }, []) — неочевидный cleanupРешение:
useUnmount(() => {}) — явная семантикаЗадача: выполнить код только при изменении зависимостей (не при монтировании)
Проблема:
useEffect(() => {}, [count]) — выполняется и при первом рендереРешение:
useUpdateEffect(() => {}, [count]) — пропускает первый рендерЗадача: упростить работу с объектами (как в классовых компонентах)
Проблема:
setPerson({ ...person, name: 'medium' })Постоянное копирование объекта
Решение:
setPerson({ name: 'medium' })Автоматическое слияние состояния
Итог
Кастомные хуки решают типовые задачи и делают код понятнее. Эти 4 хука — must-have для React-разработчика.
Please open Telegram to view this post
VIEW IN TELEGRAM
#React
🔖 Почему ваш WebSocket переподключается при каждом рендеринге?
Вы создали WebSocket, но соединение постоянно разрывается и переподключается. Проблема: React создаёт новый экземпляр сервиса при каждом рендеринге. Без useCallback сервис теряет стабильную ссылку, что приводит к переподключениям и утечкам памяти.
📎 Основные проблемы:
🔸 WebSocket переподключается, теряя состояние соединения
🔸 Память утекает: старые соединения не закрываются
🔸 Event listeners создают дубликаты
🔸 Производительность падает из-за пересоздания ресурсов
Когда компонент рендерится без useCallback, каждый раз создаётся новая функция. Для долгоживущих сервисов (WebSocket, Worker) это критично — вы разрываете всё, что было до этого, вместо переиспользования.
Ключевой момент: useRef гарантирует, что сервис существует на весь цикл компонента, а useCallback — что ссылка на функцию не меняется. Идеальное сочетание для постоянных ресурсов.
✅ Используйте useCallback + useRef для:
- WebSocket и долгоживущих соединений
- Web Workers, которые должны жить долго
- Сервисов, хранящих внутреннее состояние
- Optimization обработчиков событий
❌ Избегайте:
- Создания нового сервиса без ref
- Забывать cleanup в useEffect
- Использования useCallback без причины
- Передачи ref как пропс дочерним компонентам
Стабильная ссылка на сервис — это гарантия корректной работы!
Остальные продвинутые техники React - читай в статье⬇️
📎 Статья
🎙 Новости
📝 База вопросов
Вы создали WebSocket, но соединение постоянно разрывается и переподключается. Проблема: React создаёт новый экземпляр сервиса при каждом рендеринге. Без useCallback сервис теряет стабильную ссылку, что приводит к переподключениям и утечкам памяти.
// Плохо - сервис пересоздаётся при каждом рендеринге
function ChatComponent() {
const ws = new WebSocket('ws://server');
const sendMessage = (msg) => {
ws.send(msg); // Новый WebSocket при каждом рендере
};
return <button onClick={() => sendMessage('Hi')}>Send</button>;
}
// Хорошо - стабильная ссылка на сервис
function ChatComponent() {
const wsRef = React.useRef(new WebSocket('ws://server'));
const sendMessage = React.useCallback((msg) => {
wsRef.current.send(msg); // Один и тот же WebSocket всегда
}, []);
return <button onClick={() => sendMessage('Hi')}>Send</button>;
}
Когда компонент рендерится без useCallback, каждый раз создаётся новая функция. Для долгоживущих сервисов (WebSocket, Worker) это критично — вы разрываете всё, что было до этого, вместо переиспользования.
Ключевой момент: useRef гарантирует, что сервис существует на весь цикл компонента, а useCallback — что ссылка на функцию не меняется. Идеальное сочетание для постоянных ресурсов.
- WebSocket и долгоживущих соединений
- Web Workers, которые должны жить долго
- Сервисов, хранящих внутреннее состояние
- Optimization обработчиков событий
- Создания нового сервиса без ref
- Забывать cleanup в useEffect
- Использования useCallback без причины
- Передачи ref как пропс дочерним компонентам
Стабильная ссылка на сервис — это гарантия корректной работы!
Остальные продвинутые техники React - читай в статье
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
#JavaScript
🔖 Почему ваш JavaScript-бандлер весит как три полнометражных фильма?
Вы загрузили приложение, добавили ещё библиотек, и вот уже бандлер весит 500KB вместо 100KB. Пользователи ждут загрузки 5 секунд вместо 1 секунды. Приложение работает, но медленно. Каждый килобайт JS — это время парсинга, выполнения, разборки браузером. На мобильнике это значит батарея сливается, страница зависает, пользователи уходят к конкурентам.
Основные проблемы раздутого бандлера:
🔸 Дольше загружается на мобильных сетях (LTE, 3G)
🔸 Браузер парсит и выполняет больше кода — замораживает UI
🔸 На слабых устройствах всё работает медленнее, батарея быстрее сливается
🔸 Поисковики штрафуют за низкую производительность, рейтинг падает
Раздутый бандлер — это не просто цифра на диске. Это деньги компании (CDN трафик), разочарование пользователя (медленная загрузка), потерянные конверсии (люди ушли, не дождались).
✅ Как уменьшить бандлер:
1. Аудит зависимостей — используйте webpack-bundle-analyzer, посмотрите что там. Может быть, импортируете тяжелую библиотеку для одной функции?
2. Code splitting — разделите на чанки, загружайте по требованию. React.lazy(), динамический импорт для маршрутов.
3. Tree-shaking — удаляйте неиспользуемый код. ES модули помогают бандлеру определить, что не нужно.
4. Минификация — Terser, Gzip/Brotli. Просто сжимайте код, функциональность не меняется.
5. Server-side rendering — переложите вычисления на сервер вместо клиента. Меньше JS в браузере = быстрее загружается.
❌ Избегайте:
- Импорта целых библиотек для одной функции
- Неиспользуемого кода (закомментированные куски месячной давности)
- Отключенного tree-shaking в production сборке
- Только клиентского рендеринга для статического контента
Каждый килобайт имеет значение. Производительность = пользовательский опыт = конверсии.
📎 Статья
🎙 Новости
📝 База вопросов
Вы загрузили приложение, добавили ещё библиотек, и вот уже бандлер весит 500KB вместо 100KB. Пользователи ждут загрузки 5 секунд вместо 1 секунды. Приложение работает, но медленно. Каждый килобайт JS — это время парсинга, выполнения, разборки браузером. На мобильнике это значит батарея сливается, страница зависает, пользователи уходят к конкурентам.
// Плохо - импортируете всё, используете мало
import _ from 'lodash'; // 70KB, нужна одна функция
import * as echarts from 'echarts'; // 1MB для одного графика
import UILibrary from 'heavy-ui-kit'; // 200KB со всеми компонентами
// Хорошо - импортируйте только нужное
import { debounce } from 'lodash-es'; // Tree-shaking, 2KB
import { BarChart } from 'lightweight-charts'; // 50KB, минимум
import { Button, Input } from '@headlessui/react'; // Tree-shaking, 10KB
Основные проблемы раздутого бандлера:
Раздутый бандлер — это не просто цифра на диске. Это деньги компании (CDN трафик), разочарование пользователя (медленная загрузка), потерянные конверсии (люди ушли, не дождались).
1. Аудит зависимостей — используйте webpack-bundle-analyzer, посмотрите что там. Может быть, импортируете тяжелую библиотеку для одной функции?
2. Code splitting — разделите на чанки, загружайте по требованию. React.lazy(), динамический импорт для маршрутов.
3. Tree-shaking — удаляйте неиспользуемый код. ES модули помогают бандлеру определить, что не нужно.
4. Минификация — Terser, Gzip/Brotli. Просто сжимайте код, функциональность не меняется.
5. Server-side rendering — переложите вычисления на сервер вместо клиента. Меньше JS в браузере = быстрее загружается.
- Импорта целых библиотек для одной функции
- Неиспользуемого кода (закомментированные куски месячной давности)
- Отключенного tree-shaking в production сборке
- Только клиентского рендеринга для статического контента
Каждый килобайт имеет значение. Производительность = пользовательский опыт = конверсии.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#C++
🔖 Почему memset с целыми числами дает странные результаты?
memset — это функция для заполнения памяти значениями побайтно. Многие думают, что
🔸 Основные проблемы с memset:
🔸 memset работает побайтно, не зная о типе данных
🔸 С целыми числами только 0 и -1 работают правильно, остальное — мусор
🔸 С не-POD типами (std::string, классы) приводит к краху
🔸 Лишняя единица в size параметре — выход за границы памяти
memset принимает три параметра: указатель, значение (преобразуется в byte), размер в байтах. Проблема: для int это 4 байта. Если вы хотите установить int = 1, нужно 4 раза записать байт 1, что даст 0x01010101 = 16843009, а не 1.
🔸 Практический пример:
✅ Используйте memset для:
- Обнуления массивов и буферов (0)
- Установки всех битов в 1 (-1)
- Работы с char массивами
- Инициализации POD типов (Plain Old Data)
❌ Не делайте:
- memset с целыми числами кроме 0 и -1
- memset с std::string и классами (краш!)
- memset на не-POD структурах с конструкторами
- Ошибки в параметре size (выход за границы)
Правило: memset = только для побайтных операций. Для сложных типов используйте std::fill или конструкторы.
📎 Статья
🎙 Новости
📝 База вопросов
memset — это функция для заполнения памяти значениями побайтно. Многие думают, что
memset(arr, 1, size) заполнит целые числа единицами. На самом деле это создаст странные значения, потому что memset работает с байтами, а не с типами. Вот почему нужно понимать, как она реально работает.// Плохо - пытаетесь установить целым числам значение 1
int numbers[5];
std::memset(numbers, 1, sizeof(numbers));
std::cout << numbers[0]; // Странный результат: 16843009
// Хорошо - используйте memset только для 0 и -1
int numbers[5];
std::memset(numbers, 0, sizeof(numbers)); // Все нули
// ИЛИ
std::memset(numbers, -1, sizeof(numbers)); // Все биты = 1
memset принимает три параметра: указатель, значение (преобразуется в byte), размер в байтах. Проблема: для int это 4 байта. Если вы хотите установить int = 1, нужно 4 раза записать байт 1, что даст 0x01010101 = 16843009, а не 1.
char str[50];
std::memset(str, '*', sizeof(str)); // Работает: заполнит звездами
str[sizeof(str) - 1] = '\0';
int matrix[3][3];
std::memset(matrix, 0, sizeof(matrix)); // Работает: матрица нулей
std::memset(matrix, -1, sizeof(matrix)); // Работает: все биты = 1
// Буфер для обработки изображений
unsigned char pixels[100][100];
std::memset(pixels, 0, sizeof(pixels)); // Чёрный (0)
std::memset(pixels, 255, sizeof(pixels)); // Белый (255)
// Очистка игрового состояния
struct GameState {
int score;
int lives;
bool gameOver;
} state;
std::memset(&state, 0, sizeof(GameState)); // Работает для POD
state.lives = 3; // Затем переустанавливаете нужные значения
- Обнуления массивов и буферов (0)
- Установки всех битов в 1 (-1)
- Работы с char массивами
- Инициализации POD типов (Plain Old Data)
- memset с целыми числами кроме 0 и -1
- memset с std::string и классами (краш!)
- memset на не-POD структурах с конструкторами
- Ошибки в параметре size (выход за границы)
Правило: memset = только для побайтных операций. Для сложных типов используйте std::fill или конструкторы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#Programming
🔖 Почему ваша библиотека никто не использует, хотя она работает?
Вы написали библиотеку, которая решает проблему, но она сложная в использовании и никто не хочет её применять. Проблема не в функциональности, а в проектировании. Хорошая библиотека предоставляет точки расширения, не навязывает свой контекст и остаётся простой. Плохая — заставляет пользователя прыгать через обручи.
📎 Основные проблемы плохих библиотек:
🔸 Отсутствие точек расширения — невозможно кастомизировать
🔸 Сложная архитектура — нужно понимать всю библиотеку, чтобы использовать часть
🔸 Конкретные типы вместо обобщений — работает только для одного случая
🔸 Навязанный контекст — вынуждает пользователя следовать вашему стилю
Хорошая библиотека предполагает точку зрения пользователя, а не разработчика. Она не предполагает, что пользователь сделает. Вместо этого она предоставляет инструменты и ступеньки, позволяя каждому использовать её по-своему.
Три столпа проектирования:
✅ Проектируйте библиотеку так, чтобы:
- Компоненты были независимы и использовались отдельно
- Работала с разными типами, не только одним конкретным
- Предоставляла точки расширения (callback, custom comparator, plugin)
- Была легка в понимании — минимум контекста для начала использования
❌ Не делайте:
- Сложные архитектуры, когда вся библиотека зависит друг от друга
- Конкретные типы (int, string) вместо шаблонов и обобщений
- Фиксированное поведение без возможности кастомизации
- Навязывание дизайн-решений пользователям
Пример хорошей библиотеки — C++ STL: контейнеры работают с алгоритмами, всё обобщено через шаблоны, есть кастомные компараторы и аллокаторы. Пример плохой — фреймворк, где всё зависит от всего.
Полезная библиотека думает о том, кто её будет использовать, и делает это легко.
📎 Статья
🎙 Новости
📝 База вопросов
Вы написали библиотеку, которая решает проблему, но она сложная в использовании и никто не хочет её применять. Проблема не в функциональности, а в проектировании. Хорошая библиотека предоставляет точки расширения, не навязывает свой контекст и остаётся простой. Плохая — заставляет пользователя прыгать через обручи.
// Плохо - функция навязывает свой контекст, нет гибкости
void sort(int& container) {
// Компаратор зафиксирован, нельзя поменять
// Способ свопинга элементов фиксирован
}
// Хорошо - точки расширения встроены в интерфейс
template <class R, class C = DefaultCompare, class S = DefaultSwap>
void sort(R&& range, C compare = C(), S swap = S()) {
// Пользователь может передать свой компаратор
// Пользователь может передать свой своп
// По умолчанию работает стандартно
}
Хорошая библиотека предполагает точку зрения пользователя, а не разработчика. Она не предполагает, что пользователь сделает. Вместо этого она предоставляет инструменты и ступеньки, позволяя каждому использовать её по-своему.
Три столпа проектирования:
// 1. МОДУЛЬНОСТЬ - компоненты независимы
// Контейнеры отделены от алгоритмов, но работают вместе
std::vector<int> v = {3, 1, 2};
std::sort(v.begin(), v.end()); // Работает с любым контейнером
// 2. МНОГОРАЗОВОСТЬ - работает для многих типов
template <class T>
class Maybe {
T value; // Работает с любым типом T
};
// 3. РАСШИРЯЕМОСТЬ - встроены точки для кастомизации
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b; // Пользовательский компаратор
});
- Компоненты были независимы и использовались отдельно
- Работала с разными типами, не только одним конкретным
- Предоставляла точки расширения (callback, custom comparator, plugin)
- Была легка в понимании — минимум контекста для начала использования
- Сложные архитектуры, когда вся библиотека зависит друг от друга
- Конкретные типы (int, string) вместо шаблонов и обобщений
- Фиксированное поведение без возможности кастомизации
- Навязывание дизайн-решений пользователям
Пример хорошей библиотеки — C++ STL: контейнеры работают с алгоритмами, всё обобщено через шаблоны, есть кастомные компараторы и аллокаторы. Пример плохой — фреймворк, где всё зависит от всего.
Полезная библиотека думает о том, кто её будет использовать, и делает это легко.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
#Go
🔖 Почему Go-разработчик пишет код, который Java-девелопер считает "странным"?
Вы Java-разработчик, начали писать на Go и всё кажется странным: нет try-catch, нет наследования, строчные буквы для приватности, можно вызвать метод у nil. Go — это не Java, это другая парадигма. Явность вместо магии, композиция вместо наследования, ошибки как значения вместо исключений.
📎 Основные различия Go от Java:
🔸 Нет исключений (try-catch), ошибки — это значения (if err != nil)
🔸 Приватность через заглавность букв (Public vs private), не через модификаторы
🔸 Нет наследования, только композиция и интерфейсы
🔸 Интерфейсы неявные — не нужно писать implements
Go философия: явность над неявностью, простота над магией. Кода больше, но всё понятно. Нет аннотаций, нет DI фреймворков, нет магических прокси и рефлексии за кулисами. Вы видите, что происходит.
Два ключевых отличия:
✅ Go хорош для:
- Микросервисов и бекенда (явность, производительность)
- Когда нужна скорость разработки и чистота кода
- Когда нет необходимости в сложных абстракциях на старте
- Когда важна читаемость и простота отладки
❌ Привыкайте к:
- Повторению проверок ошибок (if err != nil много раз)
- Коротким названиям переменных (ctx, err, db, u, p)
- Отсутствию try-catch блоков (это норма)
- Приватности через буквы, а не ключевые слова
- Явным транзакциям и управлению ресурсами
Go требует переучивания мышления. Java — "дайте мне абстракции и автоматику". Go — "вот инструменты, делайте сами, но явно". Один раз адаптируетесь — полюбите за простоту и понятность.
📎 Статья
🎙 Новости
📝 База вопросов
Вы Java-разработчик, начали писать на Go и всё кажется странным: нет try-catch, нет наследования, строчные буквы для приватности, можно вызвать метод у nil. Go — это не Java, это другая парадигма. Явность вместо магии, композиция вместо наследования, ошибки как значения вместо исключений.
// Плохо (по Go стандартам) - Java способ обработки ошибок
try {
String raw = Files.readString(Path.of("cfg.json"));
Config cfg = mapper.readValue(raw, Config.class);
return cfg;
} catch (IOException ex) {
throw new IllegalStateException("cannot init config", ex);
}
// Хорошо (Go способ) - явная проверка каждой ошибки
raw, err := os.ReadFile("cfg.json")
if err != nil {
return nil, fmt.Errorf("read cfg: %w", err)
}
var cfg Config
if err := json.Unmarshal(raw, &cfg); err != nil {
return nil, fmt.Errorf("parse cfg: %w", err)
}
return &cfg, nil
Go философия: явность над неявностью, простота над магией. Кода больше, но всё понятно. Нет аннотаций, нет DI фреймворков, нет магических прокси и рефлексии за кулисами. Вы видите, что происходит.
Два ключевых отличия:
// 1. ЯВНАЯ ОБРАБОТКА ОШИБОК - видите каждый шаг
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("begin tx: %w", err)
}
defer tx.Rollback()
// 2. ПРИВАТНОСТЬ ЧЕРЕЗ БУКВУ - заглавная = public, строчная = private
type PaymentGateway interface { // Public
Charge(ctx context.Context) error
}
func (s *stripe) charge(ctx context.Context) error { // private method
// ...
}
- Микросервисов и бекенда (явность, производительность)
- Когда нужна скорость разработки и чистота кода
- Когда нет необходимости в сложных абстракциях на старте
- Когда важна читаемость и простота отладки
- Повторению проверок ошибок (if err != nil много раз)
- Коротким названиям переменных (ctx, err, db, u, p)
- Отсутствию try-catch блоков (это норма)
- Приватности через буквы, а не ключевые слова
- Явным транзакциям и управлению ресурсами
Go требует переучивания мышления. Java — "дайте мне абстракции и автоматику". Go — "вот инструменты, делайте сами, но явно". Один раз адаптируетесь — полюбите за простоту и понятность.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
CSS Grid — один контейнер делится на строки и столбцы в CSS. Все элементы располагаются как на шахматной доске.
Вместо кучи DIV'ов для раскладки:
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: 80px 1fr 50px;
grid-template-areas:
'sidebar header'
'sidebar main'
'sidebar footer';
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }Вот так! Рисуешь структуру в CSS, как картинку.
fr — "fraction", волшебный юнит Grid'а:
/* 1/4 и 3/4 пространства, автоматически пересчитывается */
grid-template-columns: 1fr 3fr;
repeat() — копирование столбцов:
/* 7 столбцов одинаковой ширины (календарь) */
grid-template-columns: repeat(7, 1fr);
Элементы занимают несколько ячеек
.child {
grid-column: 1 / 4; /* от столбца 1 до 4 */
grid-row: 1 / 3; /* от строки 1 до 3 */
}Главное
Чистый HTML, вся вёрстка в CSS!
Интерактивный гайд по ссылке ниже
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ненавидишь писать документацию? Davia её генерирует автоматически с диаграммами!
davia generate
- Онбординг новичков — они разберут проект за часы, а не за дни
- Возврат к старому коду — забыл, как работает? Есть документация!
- Крупные проекты — видишь всю архитектуру визуально
- Open source — пользователи быстрее разберутся
- Code review — проще объяснять изменения
Вместо этого:
TODO: написать документацию...
Теперь это:
Автоматическая документация с диаграммами модулей,
зависимостей, API и структуры проекта ✨
Почему это важно
Документация часто отстаёт от кода. Davia генерирует её из самого кода, поэтому она всегда актуальна!
Забирай на GitHub и пошли документировать проекты!
Please open Telegram to view this post
VIEW IN TELEGRAM