TGViewer
Channel Public Channel
StepOne | Степан Минин

StepOne | Степан Минин

@steponeit

StepOne by Степан Минин @ststphn

Твой первый шаг к успеху в программировании

Закрытый тг канал https://t.me/tribute/app?startapp=slOA

По вопросам рекламы @Spiral_Yuri

Ютуб https://www.youtube.com/@steponeit
Subscribers
3.27K
Photos
290
Videos
40
Links
357

Showing posts older than #243 · Back to latest

Older Posts 20 shown
Post #242 3.11K
StepOne | Степан Минин ASP NET 8 наконец-то добавит многообразие зависимостей 🥳 Реализовано это будет через ключи (например строковые). Возвращаясь к старому примеру, он преобразится следующим образом: public interface IDependency {} public class DependencyImplOne : IDependency…
Собрал для вас все посты канала про полиморфное поведение DI в одной статье на Хабр!

Прочитать и поставить плюсик ➕ можно по ссылке:

https://habr.com/ru/companies/ruvds/articles/776768/

P.S. Kandinsky продолжает выручать - на этот раз попробовал 3.0 версию 🤯
  • 👍 16
  • ❤ 5
  • 🔥 5
Post #241 2.46K
Дорогие подписчики StepOne 🎄

От всей души поздравляю вас с наступающим 2024 годом!

Желаю как можно меньше красноглазить с залипанием в устройства, и как можно больше проводить времени с семьёй, друзьями и близкими ❤️

Пусть у вас всё будет хорошо!

В следующем году будет много интересного...😈
  • 🎉 39
  • ❤ 8
  • 🥰 2
  • 👍 1
Post #240 2.7K
StepOne | Степан Минин Завтра начнётся последний месяц 2023 года, а значит пора подвести итоги ноября 🤩 В этом месяце постарался выдать побольше эксклюзивных материалов 🤫 🔥 Уникальный гайд по акклиматизации на новой работе 🔥 Разоблачение стагнации процессов собеседования OZON…
Год выходит на финишную прямую, как и контент в декабре на этом канале 🥶

Вот самые важные посты последнего месяца, которые можно перечитать ещё раз 🤔

⚡️ Как растёт моя архитектурная компетенция на примере пет проекта

⚡️ Хитросделанные partial'ы

⚡️ Самое понятное объяснение ковариантности и контравариантности

⚡️ Новое и абсолютно уникальное прочтение паттерна Visitor в моём исполнении
  • 🔥 8
  • 👍 3
  • ⚡ 1
  • ❤ 1
Post #239 2.24K

Forwarded from Сеньор Юникорн

Слово вкатуна. Харды на собесе

Криминальная драма, основанная на реальных событиях, все совпадения - случайны.

Отчаявшись попасть на стажировку в 😀 после соревнования Контеста, 16-летний Андрей возвращается домой и случайно видит, как пацаны за гаражами решают литкод.

Он присоединяется к пацанами, и работяги-джейсоноукладчики знакомят Андрея с учением Назрана. Благодаря им он узнает, что учить алгоритмы не обязательно (этим занимаются только стажеры-"чушпаны") - достаточно солгать в резюме про 3 года опыта, придумать 2-3 фейковых места работы - и можно будет "вкатиться" в Сбер на наносеки 200к/месяц.

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

И когда Андрей пытается «соскочить», выясняется, что теперь он должен заплатить штраф размером в полмиллиона рублей…

#пятничное от @senior_unicorn
  • 😁 23
  • 🔥 4
  • ❤ 3
  • 🥱 2
  • 👍 1
Post #238 2.16K
Что означает «CAP» в CAP теореме?

1️⃣ Consistency, Availability, Partition Tolerance

2️⃣ Control, Allocation, Process

3️⃣ Code, Architecture, Pipeline

4️⃣ Connection Access Protocol

Правильный ответ: 1️⃣

Пояснение:

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

Consistency - данные между узлами системы согласованы и не противоречат друг другу

Availability - любой request к системе завершается response

Partition Tolerance - выход из строя отдельных узлов системы не влияет на её общую работоспособность


Ставь 🐳 если тебя это спрашивали на собесе
  • 🐳 24
  • 👍 13
  • 😱 4
  • ❤ 1
  • 👌 1
Post #237 2.06K
  • 🤔 2
  • 👍 1
  • 🤯 1
  • 😱 1
  • 🙏 1
  • 🐳 1
  • 🌭 1
Post #236 2.35K
StepOne | Степан Минин Правильность архитектуры проверяется при внедрении нового функционала Кажется, написал что-то очевидное, но столкнулся с этим лично на своём pet project. В рамках него я пишу интерпретатор TypeScript подобного языка. Этот проект родился, как дипломная работа…
Visitor NET. Уникальный проект

Когда я начал рефакторинг своего интерпретатора, передо мной встал вопрос, как реализовывать паттерн Visitor.

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

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

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

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

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

public interface IVisitor<in TVisitable, out T> : IVisitor
where TVisitable : IVisitable
{
T Visit(TVisitable visitable);
}


Соответственно сам визитор выглядел примерно так:

public class TypeSystemLoader :
IVisitor<ScriptBody>,
IVisitor<AbstractSyntaxTreeNode>,
IVisitor<TypeDeclaration>
{
// ...
}


Сам посещаемый элемент должен уметь вызвать visitor.Visit(this) и для этого ему нужно знать, что у типа есть правильная перегрузка метода.

Классический ациклический вариант предлагает следующую реализацию метода Accept:

public class ScriptBody : AbstractSyntaxTreeNode
{
public override void Accept(IVisitor visitor)
{
if (visitor is IVisitor<ScriptBody> typed)
typed.Visit(this);
}
}


Однако, прибегать к явному даункастингу в такой ситуации очень не хочется, потому что это лишает код полиморфности.

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

public interface IVisitable<in TVisitor, out T> : IVisitable
where TVisitor : IVisitor
{
T Accept(TVisitor visitor);
}


Такая реализация нарушала OCP и LSP, поскольку в посещаемом элементе отсутствовала единая точка входа для любого посетителя, независимая от результата обработки.

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

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

Всего этого удалось добиться с помощью контравариантности и паттерна CRTP:

public interface IVisitor<in TVisitable, out TReturn>
where TVisitable : IVisitable<TVisitable>
{
TReturn Visit(TVisitable visitable);
}

public interface IVisitable<out TVisitable>
where TVisitable : IVisitable<TVisitable>
{
TReturn Accept<TReturn>(IVisitor<TVisitable, TReturn> visitor);
}


Тогда, в самом посещаемом элементе приходится писать два метода Accept: первый для корня иерархии, который будет вызывать посетитель; а второй уже для того, чтобы под капотом вызвался нужный Visit:

public record Operation(
char Symbol,
BinaryTreeNode Left,
BinaryTreeNode Right) : BinaryTreeNode, IVisitable<Operation>
{
public override TReturn Accept<TReturn>(
IVisitor<BinaryTreeNode, TReturn> visitor) =>
Accept(visitor);

public TReturn Accept<TReturn>(
IVisitor<Operation, TReturn> visitor) =>
visitor.Visit(this);
}


С написанием этих двух методов можно не заморачиваться, так как я создал incremental source generator, который автоматизирует написание кода.

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

При этом, подобного решения не найти на просторах интернета - осмелюсь заявить, что вы видите подобное впервые.

Поэтому, давайте поддержим мой проект звездой на гитхаб ⭐️
https://github.com/Stepami/visitor-net

Нейросеть так не сможет.
GitHub GitHub - Stepami/visitor-net: First-ever acyclic generic extensible typesafe implementation of Visitor pattern for .NET without… First-ever acyclic generic extensible typesafe implementation of Visitor pattern for .NET without any usage of dynamic cast. Implementation powered by Source Generators - Stepami/visitor-net
  • 🔥 16
  • 👍 7
  • ❤ 2
  • 🕊 2
Post #235 2.35K
Контравариантность и ковариантность 🧠

В одном из профильных чатов видел сообщение о том, что упомянутая в заголовке тема даётся чуть ли не сложнее всего, что входит в основы C#.

Иногда, кажется проще запомнить наглядно, на примерах.

Также, стоит упомянуть, что всё нижеописанное актуально именно в дженериках.

Контравариантность ⬆️

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

interface IFooIn<in TInputType>
{
void Bar(TInputType input);
}


Пример из стандартной библиотеки - IComparable<>, где благодаря контравариантности можно сделать так:

IComparable<IEnumerable<char>> charEnumerableComparable = // ...;

// ...

IComparable<string> stringComparable = charEnumerableComparable;


Ковариантность ⬇️

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

interface IFooOut<out TOutputType>
{
TOutputType Baz();
}


Пример из стандартной библиотеки - IEnumerable<>, где благодаря ковариантности можно сделать так:

IEnumerable<Task<object>> tasksWithResults = // ...;

// ...

IEnumerable<Task> tasks = tasksWithResults;


Дополнительно 🤓

Во-первых, первое и второе можно совмещать в рамках одного типа:

interface IFooInOut<in TInputType, out TOutputType>
{
TOutputType Foo(TInputType input);
}


Во-вторых, это очень мощный инструмент на стыке параметрического полиморфизма и полиморфизма подтипов, который позволяет строить мощные расширяемые и универсальные системы типов.

В-третьих, самый смак этой фичи в том, что кастинг, который был показан выше, в силу своей неявности по перфомансу равен отсутствию кастинга, поскольку в результирующем IL отсутствуют инструкции castclass.
  • 🤯 9
  • 🔥 7
  • 👍 5
  • ❤ 2
Post #234 2.06K
Тут на днях разнесли в пух и прах первичные конструкторы в C# 12

В целом я согласен со многими поинтами, и поэтому хочется посмотреть, так сказать, bigger picture.

Подумайте: есть ли у вас такое ощущение, что последней крупной и продуманной до конца фичей была добавленная в C# 5 асинхронность?

Все последующие обновления чувствуются минорными - их спокойно можно было бы нумеровать 5.1, 5.2, 5.3 и так далее.

Даже обобщённую математику не доделали до конца!

Скорее всего, проблема в том, что Андерс Хейлсберг не занимается проектом, зато о TypeScript теперь кричат из каждого утюга 🤔

https://habr.com/ru/articles/779166/
  • 👍 8
  • 💯 5
  • ❤ 1
  • 🙏 1
Post #233 2.17K
Лучшая статья за прошедшую неделю 👍

Невольно закралась мысль о том, что все мои последние проекты могли прекрасно существовать в виде монолитов...

"Вопрос: Может ли монолит быть также эффективен как микросервисная архитектура?

Ответ: Нет, ему никогда не стать настолько же медленным
" 👏

https://habr.com/ru/articles/779362/
  • 👍 9
  • 😁 8
  • 👏 1
  • 💯 1
Post #232 2.3K
StepOne | Степан Минин Отлаживать код в Rider смогут даже младенцы 👶 JetBrains продолжают развивать лучшую IDE для C# разработчиков и удивлять их новыми обновлениями. В грядущей версии 2023.3 завезут, на мой взгляд, настоящую killer feature, которая называется Debugger Data Flow…
Релизнулся Rider 2023.3 😱

Завезли много всего интересного: поддержку .NET 8 SDK, одновременный запуск нескольких проектов, предиктивный дебаггинг, вытащили из превью AI ассистента и многое другое

Например, добавили отрисовку диаграм зависимостей типов 🎁

Теперь можно воочию увидеть архитектуру своего проекта, и убедиться, что её нужно переделать 😱

Короче говоря, побежали обновляться! 🏃‍♂️
  • 🔥 17
  • 😁 3
  • ❤ 1
  • 🤯 1
Post #231 2.21K
partial взрывает мне мозг 🤯

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

В коммерческой практике не так часто это встречалось.

И я обнаружил, что с этой фичей можно делать просто супер странные вещи.

Например, у нас есть простенький интерфейс IConract:

public interface IContract
{
void Foo();
}


Сделаем раздробленный класс MyPartialClass:

public partial class MyPartialClass
{
// ...
}


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

public partial class MyPartialClass : IContract
{
public virtual void Foo()
{
throw new NotImplementedException();
}
}


При этом виртуальный метод Foo можно спокойно переопределять в той же манере в наследниках:

public partial class MyPartialClassDerived : MyPartialClass
{
public override void Foo()
{
Console.WriteLine(this);
base.Foo();
}
}


Опять же, догадаться о существовании подобных членов типа будет достаточно проблематично.

Злоупотреблять таким в командной работе конечно же не рекомендую.

Однако, подобный функционал будет полезен разработчикам решений на основе Source Generators.

Таким образом, можно докидывать компилятором различные бойлерплейты, которые не хочется писать руками.
  • 💯 9
  • 🤯 3
  • 🙏 2
Post #230 1.84K
В EF Core 8 узаконили Value Object 🤩

Одно из тех обновлений, которое не следует пропускать 😱

Например, есть следующая сущность заказа Order вместе с сущностью клиента Customer:

public class Customer
{
public int Id { get; set; }
public required string Name { get; set; }
public required Address Address { get; set; }
public List<Order> Orders { get; } = new();
}

public class Order
{
public int Id { get; set; }
public required string Contents { get; set; }
public required Address ShippingAddress { get; set; }
public required Address BillingAddress { get; set; }
public Customer Customer { get; set; } = null!;
}


Кажется, что уже существует семантика работы с типами значениями, ведь никто не отменял атрибут [Owned].

Однако, при попытке сохранить сущность, которая использует один инстанс на несколько полей возникнет ошибка:

var address = GetAddress();

customer.Orders.Add(
new Order { Contents = "Tesco Tasty Treats", BillingAddress = address, ShippingAddress = address, });

await context.SaveChangesAsync();


Кажется, что всё будет хорошо, но получим исключение InvalidOperationException с сообщением

«Cannot save instance of 'Order.ShippingAddress#Address' because it is an owned entity without any reference to its owner. Owned entities can only be saved as part of an aggregate also including the owner entity.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.PrepareToSave()»

Теперь, достаточно разметить тип данных атрибутом [ComplexType].
При этом он может быть иммутабельным!

[ComplexType]
public record Address(string Line1, string? Line2, string City, string Country, string PostCode);


Соответственно, такое обновление сущности:

customer.Address = customer.Address with { Line1 = "Peacock Lodge" };

await context.SaveChangesAsync();


Сгенерирует следующий SQL:

UPDATE [Customers] SET [Address_Line1] = @p0
OUTPUT 1
WHERE [Id] = @p1;


То есть, это собственный тип на стероидах 💉
  • 🔥 11
  • 👍 2
  • 🎉 1
  • 🤣 1
  • 🏆 1
Post #229 1.9K
StepOne | Степан Минин Немного о pet project В прошлом посте я написал про то, как опыт коммерческой разработки повлиял на мой стиль его ведения. Однако, может возникнуть закономерный вопрос: «Так чем ты там занимаешься?» Отвечаю. Сейчас мой основной open source contribute -…
Правильность архитектуры проверяется при внедрении нового функционала

Кажется, написал что-то очевидное, но столкнулся с этим лично на своём pet project.

В рамках него я пишу интерпретатор TypeScript подобного языка.

Этот проект родился, как дипломная работа в бакалавриате бауманки.

Но поскольку сроки были сильно сжаты, то изначально всё сделано на скорую руку.

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

Одно из самых неправильных решений в проекте - обработка абстрактного синтаксического дерева интрузивным методом.

Подсветить то, насколько важно перейти на паттерн Visitor, помогла моя попытка внедрить DDD и выделить домен системы.

Сейчас, когда рефакторинг близится к концу, все детали пазла собираются в одну картину, новые фичи собираются на глазах за счёт правильной траектории архитектурной эволюции.

При этом стали видны новые точки роста - ошибки в реализации «Посетителя», изоляция домена, изменение знания одних частей системы о других и так далее.
  • 👍 10
  • 🔥 4
  • 👏 2
  • ❤ 1
Post #228 1.97K
StepOne | Степан Минин В Москве уже успело выпасть 7 сантиметров снежного покрова, а я успел собрать для вас самые важные посты октября 🤩 💎 Запись моего спикерского дебюта на Стачке 💎 История о том, как этот дебют состоялся 💎 Самое простое объяснение примитивов синхронизации…
Завтра начнётся последний месяц 2023 года, а значит пора подвести итоги ноября 🤩

В этом месяце постарался выдать побольше эксклюзивных материалов 🤫

🔥 Уникальный гайд по акклиматизации на новой работе

🔥 Разоблачение стагнации процессов собеседования OZON

🔥 Запись доклада на MskDotNet Meetup

🔥 Личная история о важности открытости в зарплатных переговорах

🔥 Эффективные секретные материалы о зарплатных переговорах впервые в telegram
  • 🔥 6
  • 🥱 2
  • ❤‍🔥 1
  • ⚡ 1
Post #227 2.15K
StepOne | Степан Минин Почему я всегда сообщаю рекрутеру зарплатные ожидания? 🤔 Есть распространённое мнение о зарплатных переговорах: «кто первый назвал цифру, тот и проиграл». Однако это не совсем так. Во-первых, сама культура подобных переговоров в РФ находится в достаточно…
На просторах рунета этого не найти 🤫

Под вчерашним постом развернулась горячая дискуссия о переговорах по зарплате🔥

Поэтому делюсь с вами топом своих любимых материалов по этому вопросу ⭐️

▪️https://www.kalzumeus.com/2012/01/23/salary-negotiation/

▪️https://bayareabelletrist.medium.com/how-i-negotiated-a-software-engineer-offer-in-silicon-valley-f11590f5c656

▪️https://faangpath.medium.com/how-to-negotiate-salary-offer-e9475ab11e3f

▪️https://www.freecodecamp.org/news/how-not-to-bomb-your-offer-negotiation-c46bb9bc7dea#.w30tfvb9r
  • ❤ 7
  • 👍 6
  • 🔥 4
Post #226 2.11K
Почему я всегда сообщаю рекрутеру зарплатные ожидания? 🤔

Есть распространённое мнение о зарплатных переговорах: «кто первый назвал цифру, тот и проиграл».

Однако это не совсем так.

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

Во-вторых, разработчики это такие же рядовые сотрудники, как и другой персонал: для них существует вполне конкретный ФОТ.

То есть торги - это инструмент не растяжения мешка с деньгами, а прощупывания его границ.

И вот однажды я собеседовался в дом рф, попробовав всеми правдами и неправдами скрыть цифру 💵

Изворачивался и уходил от ответа так, как никогда в жизни 🤯

При этом тех собес был очень приятный и даже нетипичный для индустрии, перспективный проект и интересными задачами, да и ребята показались вполне адекватными…

Но когда дело дошло до оффера, меня брали по верху вилки, но оказался таким, что смысл туда идти пропал абсолютно 🥵

Сказать, что чувствовал себя тогда идиотом - не сказать ничего 😁

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

А терять время не хочет никто.
  • 👍 21
  • 🔥 4
  • 💯 4
Post #225 2.04K
StepOne | Степан Минин Внедрение конкретной реализации в стандартном контейнере Допустим, у нас есть некоторый интерфейс, который имеет несколько реализаций: public interface IDependency {} public class DependencyImplOne : IDependency {} public class DependencyImplTwo : IDependency…
ASP NET 8 наконец-то добавит многообразие зависимостей 🥳

Реализовано это будет через ключи (например строковые).

Возвращаясь к старому примеру, он преобразится следующим образом:

public interface IDependency {}

public class DependencyImplOne : IDependency {}
public class DependencyImplTwo : IDependency {}

builder.Services.AddKeyedSingleton<IDependency, DependencyImplOne>("one");
builder.Services.AddKeyedSingleton<IDependency, DependencyImplTwo>("two");


Далее, использовать вот так, с помощью атрибута [FromKeyedServices]:

public class BarService : IBarService
{
public BarService([FromKeyedServices("one")] IDependency dependency) // DependencyImplOne
{
}
}

public class BazService : IBazService
{
public BazService([FromKeyedServices("two")] IDependency dependency) // DependencyImplTwo
{
}
}


Для меня, как поклонника ООП, это знаковая веха в развитии платформы 🔥

Считаю, что ради этой киллер фичи можно смело планировать переезд на новый LTS релиз!
Docs What's new in ASP.NET Core in .NET 8 Learn about the new features in ASP.NET Core in .NET 8.
  • 🔥 24
  • 👍 8
  • 🤯 4
  • 😱 2
Post #224 1.95K
Task vs ValueTask

Что такое Task знают все C# разработчики - класс, олицетворяющий длительную операцию в асинхронном программировании.

А вот в 7 версии языка появилась структура ValueTask, и до сих пор не всем ясно её назначение.

Из интересного в ней два поля - первое для хранения задачи, второе для хранения результата (при наличии).

Поскольку Task это класс, то его экземпляр хранится в куче, а значит высвобождением памяти будет заниматься GC.

ValueTask это структура, а значит вероятнее всего окажется на стеке, и расходов на высвобождение памяти будет потрачено существенно меньше.

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

Однако, есть ряд ограничений, то есть, нельзя делать следующие вещи:

⭐️ Ожидать задачу конкурентно или несколько раз

⭐️ Вызывать метод AsTask несколько раз

⭐️ Использовать .Result или .GetAwaiter().GetResult(), если задача не завершена

⭐️ Использовать задачу в комбинаторах WhenAll() и WhenAny()
  • 👍 19
  • ❤ 3
  • 👏 3
Post #223 2K
StepOne | Степан Минин mskdotnet_meetup_55_обобщенная_математика.pdf
Долгожданная запись митапа наконец доступна!

На доклад заглянул аж сам Кирилл Маурин!

Маэстро любезно помог ответить на ряд вопросов и дополнил мой рассказ своими интересными комментариями, от которых история стала ещё глубже 🔥

Обязательно к просмотру 👨‍💻
YouTube Степан Минин "Как в C# 11 появилась обобщённая математика" В рамках доклада мы рассмотрим с нуля концепцию обобщённой математики, как она выглядит в C# 11, как могла выглядеть в предыдущих версиях языка и почему вообще появилась. Также зароемся в кишки System.Numerics, и узнаем, в какой продакшн в скором времени…
  • 🔥 10
  • 👍 3
  • ❤ 1
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →