CTO tech
198 subscribers
2 photos
4 links
Плох тот разработчик, кто не хочет стать CTO.

Сложное — просто, простое — эффективно. CTO_tech.
Download Telegram
Channel created
Channel name was changed to «CTO»
Go библиотека unsafe

С помощью пакета unsafe можно делать множество интересных хаков без оглядки на систему типов Go. Он дает доступ к низкоуровневому АПИ почти как в C. Но использование unsafe - это легкий способ выстрелить себе в ногу, поэтому нужно соблюдать определенные правила. При написании такого кода очень легко совершить ошибку.

Есть определенные риски использования unsafe pointers, один из них это то что библиотека не защищена гарантией обратной совместимости Go 1 и будущее программ Go. To есть код зависящий от unsafe pointers может сломаться в будущих версиях Go.

Примеры использования:
Любой указатель Go может быть преобразован в
unsafe.Pointer:
import (
"fmt"
"unsafe"
)
type TestStruct struct {
b1 byte
b2 byte
i int16
}

func main() {
testStruct := TestStruct{
b1: 2,
b2: 4,
i: 261,
}

type arrayType [4]byte

fmt.Println("size = ", unsafe.Sizeof(testStruct))
// size = 4
up := unsafe.Pointer(&testStruct)
testByteArray := *(*arrayType)(up)
fmt.Println("отображаем массив байт = ",
testByteArray,
)
// отображаем массив байт = [2 4 5 1]
}

Ещё пример:
func main() {
s := "foo"
b := []byte(s)
x := struct{ a int, b bool}{1, true}
sp := unsafe.Pointer(&s)
bp := unsafe.Pointer(&b)
xp := unsafe.Pointer(&x)
println(sp, bp, xp)
}
// адреса будут отличаться
// 0x14000052710 0x14000052720
// 0x140000526f8

@cto_tech
Побитовые операции в Go

Побитовые операции выполняются над отдельными разрядами чисел в бинарном представлении. Например, число 5 в двоичной системе имеет три разряда: 101, а число восемь - четыре разряда: 1000.

Операции сдвига:
<<
>>

Сдвигает битовое представление числа, например:

var b int = 2 << 2;           // 2 это 10  на два разрядов влево = 1000 или 8
var c int = 16 >> 3; // 16 это 10000 на три разряда вправо = 100 или 2


Поразрядные операции:
& - операция И или поразрядное умножение;
| - операция ИЛИ или поразрядное сложение;

^ - поразрядное исключающее ИЛИ. Возвращает 1, если только один из соответствующих разрядов обоих чисел равен 1;
&^ - сброс бита (И НЕ);
Примеры:


func main() {
var a int = 5 | 2; // 101 | 010 = 111 или 7
var b int = 6 & 2; // 110 & 010 = 10 или 2
var c int = 5 ^ 2; // 101 ^ 010 = 111 или 7
var d int = 5 &^ 6; // 101 &^ 110 = 001 или 1
}

@cto_tech
1
Паттерны проектирования

Структурный паттерн Адаптер

Adapter — структурный шаблон проектирования, предназначенный для организации использования функций объекта, недоступного для модификации, через специально созданный интерфейс. Например реализация определенного интерфейса для класса или структуры
.

Рассмотрим пример на go:

// интерфейс структуры для адаптера
type Target interface {
Operation()
}

// адаптируемая структура
type AdapteeStruct struct {
some int
}

// Метод адаптируемой структуры, который нужно вызвать где-то
func (adaptee *AdapteeStruct) AdaptedOperation() {
fmt.Println("I am AdaptedOperation()")
}


Далее мы адаптируем функционал под наши нужды:
// Структура конкретного адаптера
type ConcreteAdapter struct{
*AdapteeStruct
}

// реализация метода интерфейса, реализующего вызов адаптируемого класса
func (adapter *ConcreteAdapter) Operation() {
adapter.AdaptedOperation()
}

// основной метод для демонстрации
func main() {
fmt.Println("\nAdapter demo:\n")
adapter := NewAdapter(&Adaptee{})
adapter.Operation()
}

@cto_tech
Структуры Go

Структура (struct) используется для создания коллекции элементов разных типов данных в одной переменной.

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

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

В Go любая более-менее сложная сущность так или иначе представляет собой структуру.

type some struct {
a int
b string
c bool
}
func main() {
s := some{1, "hello", true}
println(s.a, s.b, s.c)
// 1 hello true
}


@cto_tech
Паттерн проектирования Мост

Мост (Bridge) — структурный шаблон проектирования, используемый в проектировании программного обеспечения чтобы «разделять абстракцию и реализацию так, чтобы они могли изменяться независимо». Шаблон мост использует инкапсуляцию, агрегирование и может использовать наследование для того, чтобы разделить ответственность между классами.
Среда выполнения Go и его конкурентность абсолютно феноменальны. Сложно представить возвращение к JS у которого среда выполняния Node/deno.

Полностью поддерживаю ⚡️
Please open Telegram to view this post
VIEW IN TELEGRAM
Паттерн Мост на typescript



interface IProvider {
sendMessage(messsage: string): void;
connect<T>(config: T): void;
disconnect(): void;
}

class TelegramProvider implements IProvider {
connect<T>(config: T): void {
console.log(config)
}
disconnect(): void {
console.log(`Disconnected TG`)
}
sendMessage(messsage: string): void {
console.log(messsage)
}
}

class WhatAppPRovider implements IProvider {
connect<T>(config: T): void {
console.log(config)
}
disconnect(): void {
console.log(`Disconnected WA`)
}
sendMessage(messsage: string): void {
console.log(messsage)
}
}

class NotificationSender {
constructor (private provider: IProvider) {}

send() {
this.provider.connect('connect')
this.provider.sendMessage('message')
this.provider.disconnect()
}
}

class DelayNotificationSender extends NotificationSender {
constructor(provider: IProvider) {
super(provider)
}

sendDeployed() {}
}

const sender = new NotificationSender(new TelegramProvider())
sender.send();

const sender2 = new NotificationSender(new WhatAppPRovider())
sender2.send();

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

Суть паттерна
Создать общий интерфейс для каждого из класса сервиса, где описать все методы API. Реализовать каждый класс, такого сервиса через имплементацию этого интерфейса, после чего реализовать класс, в конструктор которого передается этот же интерфейс и в общие методы API группируются в один метод, при этом этот метод в себе ссылается на this аргумента реализующий этот интерфейс.

Пример реализации
Приложение, по отправке сообщений. При этом оно должно реализовывать функциональность мгновенных и отложенных сообщений, а так же реализовывать эту функциональность для разного типа мессенджеров.
Паттерн проектирования Компоновщик (Composite)
#GOF
#structural_pattern

Компоновщик (Composite pattern) — структурный шаблон проектирования, объединяющий объекты в древовидную структуру для представления иерархии от частного к целому. Компоновщик позволяет клиентам обращаться к отдельным объектам и к группам объектов одинаково.
Channel name was changed to «Плох тот разработчик, кто не хочет стать CTO.»
Channel name was changed to «Плох тот разработчик, кто не хочет стать CTO»
Компоновщик (Composite): Как работать с иерархией объектов просто

Пример из жизни:

Представьте себе, что у нас есть ERP-система, где нужно организовать структуру иерархии компании. В ней есть отделы (например, "Маркетинг", "Разработка"), у которых могут быть вложенные подразделения, а также отдельные сотрудники.

🔧 Реализация на TypeScript

Давайте реализуем паттерн "Компоновщик" для структуры компании. У нас будут:
1. Общий интерфейс CompanyComponent.
2. Отделы как составные элементы (Department).
3. Сотрудники как листья дерева (Employee).


// Общий интерфейс Компании
interface CompanyComponent {
 getName(): string;
 getDetails(): string;
}

// Лист дерева — сотрудник
class Employee implements CompanyComponent {
 constructor(private name: string, private position: string) {}

 getName(): string {
  return this.name;
 }

 getDetails(): string {
  return `${this.name}, Позиция: ${this.position}`;
 }
}

// Ветвь дерева — отдел
class Department implements CompanyComponent {
 private children: CompanyComponent[] = [];

 constructor(private name: string) {}

 getName(): string {
  return this.name;
 }

 add(component: CompanyComponent): void {
  this.children.push(component);
 }

 remove(component: CompanyComponent): void {
  this.children = this.children.filter(child => child !== component);
 }

 getDetails(): string {
  const subordinates = this.children
   .map(child => " " + child.getDetails())
   .join("\n");

  return `Отдел: ${this.name}\nСостав:\n${subordinates}`;
 }
}


🚀 Пример использования

Реализуем компанию с несколькими отделами и сотрудниками:


// Создаем сотрудников
const employee1 = new Employee("Алиса", "Разработчик");
const employee2 = new Employee("Боб", "Дизайнер");
const employee3 = new Employee("Чарли", "Тестировщик");

// Создаем отдел Разработки
const devDepartment = new Department("Разработка");
devDepartment.add(employee1);
devDepartment.add(employee2);
devDepartment.add(employee3);

// Создаем отдел Маркетинга
const marketingDepartment = new Department("Маркетинг");
marketingDepartment.add(new Employee("Денис", "Маркетолог"));

// Создаем главный отдел Компания
const company = new Department("Компания");
company.add(devDepartment);
company.add(marketingDepartment);

// Выводим дерево компании
console.log(company.getDetails())


Результат:


Отдел: Компания
Состав:
Отдел: Разработка
Состав:
Алиса, Позиция: Разработчик
Боб, Позиция: Дизайнер
Чарли, Позиция: Тестировщик
Отдел: Маркетинг
Состав:
Денис, Позиция: Маркетолог


Важные моменты

1. Модульность: Декомпозируете всех сотрудников и отделы, избегая хаоса.
2. Удобство: Работаете с группами и отдельными элементами одинаково через общий интерфейс.
3. Реальная польза: Например, в ERP-системах вы можете динамически строить древовидные структуры (например, подотчеты или фильтры для UI).
1
RTK Query: управление запросами в React просто и удобно


Мир вам!

Если вы уже писали сложные React-приложения, то точно сталкивались с вопросом: "Как лучше управлять запросами к API?" Сделать десятки useEffect для каждого запроса? Ставить состояние в React Context? Или еще хуже — вручную отслеживать loading, error и data?

Весь этот "велосипед" выглядит как костыль.

У меня есть опыт использование React query. Основная идея подобных инструментов это разделение управлением состоянием и кэша.

Если у вас уже есть проект, который использует Redux, то тут на помощь приходит RTK Query.

Давайте разберемся, почему он так хорош.

🔍 Проблема, которую решает RTK Query

RTK Query автоматически кеширует данные, отслеживает статусы запросов (isLoading, isSuccess, isError — всё готово из коробки) и работает с Redux Store без лишней головной боли. Это особенно удобно для приложений с "живыми" данными.

🚀 Как использовать RTK Query?

1. Устанавливаем зависимости
npm i @reduxjs/toolkit react-redux


2. Создаем API с помощью createApi
RTK Query позволяет компактно описать все запросы, связанные с определенным API (например, список пользователей):
// api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const userApi = createApi({
reducerPath: 'userApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com/' }),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => 'users',
}),
}),
});

export const { useGetUsersQuery } = userApi;

Этот файл — настоящая магия. Мы только что задали:

- Base URL для всех запросов (https://jsonplaceholder.typicode.com/);
- "Эндпоинт" getUsers, который забирает /users
И всё это всего за пару строчек.

3. Подключаем API в Redux Store
Теперь передаём API в store (да-да, Redux всё ещё нужен). В моем приложении это выглядит так:
// store.ts
import { configureStore } from '@reduxjs/toolkit';
import { userApi } from './api';

export const store = configureStore({
reducer: {
[userApi.reducerPath]: userApi.reducer, // Добавляем в Redux Store
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(userApi.middleware), // Подключаем middleware
});


Рутовый компонент оборачиваем Provider:
import { Provider } from 'react-redux';
import { store } from './store';

<Provider store={store}>
<App />
</Provider>;


4. Используем запрос в компоненте

Теперь финальная часть: мы подключаем хук useGetUsersQuery (он автоматически сгенерирован RTK Query!). И просто получаем данные с API:
import React from 'react';
import { useGetUsersQuery } from './api';

export const Users = () => {
const { data, error, isLoading } = useGetUsersQuery();

if (isLoading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error.message}</p>;

return (
<ul>
{data.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};

- Хотите статус загрузки? Уже есть isLoading.
- Надо обработать ошибку? Используйте error.
- Ваши данные готовы к отображению в JSX на любом экране.



💡 Выводы
RTK Query:

1. Незаменимая штука для проектов с REST API и Redux как state manager.
2. Экономит кучу времени на запросы, их кеширование и статусы.
3. Подходит как для MVP, так и для сложных приложений (CRM или даже ERP).

Вместо ручного управления состоянием запросов и кешем вы получаете инструмент, который берёт это на себя. А вы в это время пишете бизнес-логику.

У RTK Query есть ещё множество других полезных фич, можете посмотреть доку:
- Документация RTK Query
- Примеры кода на GitHub

Сложное — это просто временная сложность.
✍️ Где я использовал RTK Query в реальных проектах?

На одном из проектов я столкнулся с достаточно большим легаси, где всё крутилось на redux-saga. Главная проблема была в том, что запросы к API реализовывались совершенно разным образом, в зависимости от настроения разработчика. Как итог — не было единого стандарта.

Мы с командой хотели:

1. стандартизировать подход к запросам,
2. сохранить элементы бизнес-логики, которые были привязаны к API,
и при этом не сломать существующую логику всего приложения.

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

🛠 Как мы внедрили RTK Query

Стандарт использования RTK Query я описал в предыдущем посте плюс вы можете глянуть доку.
Сейчас покажу, как мы вынесли всю обработку запросов в единое место, чтобы приложение оставалось стабильным и читаемым.

Шаг 1. Настраиваем baseQuery

Главная идея: всё, что можно стандартизировать, — выносим в базис.

Вот пример настройки baseQuery. Мы задали общий обработчик запросов и ошибок в коде, где всё управление запросами идет через Axios.

import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "../services/baseQuery";

export const apiSlice = createApi({
reducerPath: "apiReducer",
baseQuery,
tagTypes: ["users", "someTags"], // теги наших запросов
endpoints: () => ({})
});


Шаг 2. Реализация baseQuery

Как выглядит сам baseQuery? Вот сам код (да, он достаточно большой, но универсальный):

Теперь сам baseQuery (осторожно, много кода):
// src/services
// использую axios, в основном из-за интерцептора
import type { AxiosRequestConfig, AxiosError } from "axios";
import type { BaseQueryFn } from "@reduxjs/toolkit/query/react";

const axiosBaseQuery = ({ baseUrl }: { baseUrl: string } = { baseUrl: "" }) =>
async ({ url, method, data, params, headers }) => {
// Ваш стандартный Axios-запрос
};

const baseQueryInstance = axiosBaseQuery({ baseUrl: process.env.BACKEND_URL || "https://api.example.com" });

// Глобальная обработка ошибок
const baseQueryWithErrorHandler = async (args, api, extraOptions) => {
const result = await baseQueryInstance(args, api, extraOptions);
if (!result.error) return result;

// Обрабатываем ошибки статусов и коды
return handleQueryError(result.error);
};


Вся магия (или её отсутствие 😄) происходит в handleQueryError: обработчик статусов и сообщений ошибок. Если, например, сервер вернет статус 401, можно сразу задать глобальную логику (например, редирект на логин).

Шаг 3. Почему это удобно?

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

Да, это ещё не покрывает все возможные кейсы. Но это не цель. Главное — обезопасить ключевые моменты API и создать легкий "путь" для API-интеграций.

Пример подхода: при ошибке 403 можно сразу задать Redux экшн setErrorAction, чтобы, скажем, показать toast с ошибкой или редиректнуть пользователя.

🎯 Итог

Почему это стоит попробовать?

1. Упрощает работу с запросами через единое управление API.
2. Быстро подключается в legacy проекты.
3. Убирает хаос из кода, делает его предсказуемым (особенно для новых разработчиков)

Вопросы, комментарии или советы — пишите в комментариях. 👇 Рад послушать, как вы используете RTK Query в своих проектах.
2
Channel name was changed to «CTO tech»
Задачка: Что отрендерится на этой странице?

Вы работаете с RTK Query и хотите показать список продуктов из API. Ваш текущий код выглядит так:


import React from 'react';
import { useGetProductsQuery } from './api';

export const Products = () => {
const { data, error, isLoading } = useGetProductsQuery();

if (isLoading) return <p>Загрузка данных...</p>;
if (error) return <p>Ошибка: {error.status}</p>;
if (!data || data.length === 0) return <p>Продукты не найдены!</p>;

return (
<ul>
{data.map((product) => (
<React.Fragment key={product.id}>
<li>{product.name}</li>
</React.Fragment>
))}
</ul>
);
};


А вот сам эндпоинт API, который вы используете:


export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: 'https://fakeapi.com',
}),
endpoints: (builder) => ({
getProducts: builder.query({
query: () => 'products',
}),
}),
});

export const { useGetProductsQuery } = api;


Ваш API возвращает следующий ответ:

[
{
"id": 1,
"name": "Смартфон"
},
{
"id": 2,
"name": "Ноутбук"
},
{
"id": 3,
"name": "Чайник"
}
]


На странице Products компонента, что будет отображено пользователю?
Стоит ли применять библиотеки управляющие запросами такие как react-query, rtk-query, swr?

Ассаляму алейкум

Мой ответ — однозначно да, если вы взаимодействуете с сервером.

Несколько причин почему:

1️⃣. Уменьшаем повторяемость шаблонного кода. Это отслеживания состояний ожидания, ошибки, полученных ответов.

2️⃣. Использование всевозможного функционала, встроенных в библиотеки. Это повторяемость запросов, передача аргументов и перезапрос при обновлении их, очищение или сохранения данных при удалении компонента, инвалидация запроса при различных событиях. И это только малая часть дополнительного функционала, при том что они протестированы и проверены другими пользователями, в отличие от того что, если мы сами будем пилить нужный функционал.

3️⃣. Приносим определенный стандарт в код.
И это серьезный аргумент. Зачастую люди с разным опытом работают над проектом, соответственно и стиль кода и предпочтения разные. А используя одну из этих библиотек, мы принимаем определенную философию по использованию инструментов. Это поможет в целом стандартизировать наш код.

4️⃣. Получив экспертизу в этих инструментах, мы улучшим наше резюме.
Многие крупные компании используют эти библиотеки и в вакансиях часто указывают их в описаниях.
Таким образом, нам, как разработчикам выгодно не просто использовать инструменты запросов, но и быть экспертами в этой области. А ещё лучше быть одним из контребьютеров.
Please open Telegram to view this post
VIEW IN TELEGRAM
Как взаимодействуют frontend и backend ?

Ассаляму алейкум

(Подсмотрел статью у коллег. Отличный материал, ссылка на источник в конце.)

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

Архитектура взаимодействия

Фронтенд
: Это часть веб-приложения, с которой взаимодействует пользователь. Она включает HTML, CSS и JavaScript, а также фреймворки и библиотеки, такие как React, Vue.js и Angular. Фронтенд отвечает за отображение данных, обработку событий и обеспечение интерактивности.

Бэкенд: Это серверная часть веб-приложения, которая управляет бизнес-логикой, обработкой данных и взаимодействием с базой данных. Бэкенд может быть написан на разных языках программирования, таких как C#, Python, Ruby, Java, PHP и других. Он включает веб-серверы, базы данных и API.

HTTP-запросы и ответы
Фронтенд и бэкенд взаимодействуют через HTTP-запросы и ответы. Фронтенд отправляет запросы на сервер (бэкенд), который обрабатывает их и отправляет ответы обратно на фронтенд.

Запрос данных:
- Фронтенд отправляет HTTP GET-запрос на сервер, чтобы получить данные.
- Бэкенд получает запрос, извлекает данные из базы данных и отправляет их обратно в виде JSON-ответа.

// Фронтенд (JavaScript с использованием Fetch API)
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log(data); // Обработка данных на фронтенде
})
.catch(error => {
console.error('Error:', error);
});


Отправка данных:
- Фронтенд отправляет HTTP POST-запрос на сервер с данными для создания нового ресурса.
- Бэкенд получает запрос, обрабатывает данные и сохраняет их в базе данных, затем отправляет ответ о статусе операции.

// Фронтенд (JavaScript с использованием Fetch API)
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => {
console.log(data); // Обработка ответа на фронтенде
})
.catch(error => {
console.error('Error:', error);
});


WebSocket
Для взаимодействия в реальном времени между фронтендом и бэкендом используется WebSocket. Он позволяет устанавливать постоянное двустороннее соединение между клиентом и сервером, что полезно для приложений с обновлениями в реальном времени, таких как чаты или онлайн-игры.

// Фронтенд (JavaScript с использованием WebSocket API)
const socket = new WebSocket('ws://example.com/socket');

socket.onopen = () => {
console.log('WebSocket is open now.');
socket.send(JSON.stringify({ message: 'Hello Server!' }));
};

socket.onmessage = (event) => {
console.log('Received:', event.data);
};

socket.onclose = () => {
console.log('WebSocket is closed now.');
};


RESTful API и GraphQL

RESTful API: Это стиль архитектуры API, который использует стандартные HTTP методы (GET, POST, PUT, DELETE) для взаимодействия с ресурсами. Каждый ресурс идентифицируется уникальным URL, а данные передаются в формате JSON или XML.

GraphQL: Это язык запросов для API, который позволяет клиенту запрашивать именно те данные, которые ему нужны. В отличие от REST, где каждый ресурс имеет свой URL, в GraphQL есть единая точка доступа (endpoint), и запросы могут быть более гибкими и оптимизированными.

👉 @seniorFront