💻📖 hacker-laws

All Contributors

Законы, теории, принципы и модели, которые полезно знать разработчику.



Вступление

Существует много законов, которые люди обсуждают, говоря о разработке. Этот репозиторий собрал в себе ссылки и обзоры наиболее распространённых. Пожалуйста, делитесь им и присылайте PR’ы!

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

Законы

Ну, поехали!

Закон Амдала

Закон Амдала в Википедии

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

Давайте для наглядности приведём пример. Если программа состоит из двух частей: части А, которая должна выполняться одним процессором, и части Б, которая может выполняться параллельно, тогда мы увидим, что добавление нескольких процессоров в систему может иметь ограниченное преимущество. Это потенциально может ускорить выполнение части Б, но скорость выполнения части А останется неизменной.

Диаграмма ниже показывает несколько примеров потенциального увеличения скорости:

Диаграмма: Закон Амдала

(Источник изображения: авторство Daniels220, взято из Английской Википедии, Creative Commons Attribution-Share Alike 3.0 Unported, https://en.wikipedia.org/wiki/File:AmdahlsLaw.svg)

Как можно видеть, программа с возможностью распараллеливания на 50% принесет очень мало пользы, всего 10 процессорных единиц, тогда как программа с возможностью распараллеливания на 95% может привести к значительному улучшению скорости на более чем тысячу процессорных единиц.

В то время как Закон Мура замедляется, а скорость отдельных процессоров уменьшается, распараллеливание является ключом к повышению производительности. Этому можно считать отличным примером графическое программирование. C современными вычислениями на основе шейдеров отдельные пиксели или фрагменты могут отображаться параллельно — вот почему современные графические карты часто имеют много тысяч процессорных ядер (графических процессоров или шейдерных блоков).

Читайте также:


Закон Брукса

Закон Брукса в Википедии

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

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

Распространённое выражение «Девять женщин не могут выносить ребёнка за один месяц» отсылает нас как раз к закону Брукса. В частности, к тому факту, что некоторые виды работ нельзя поделить на части и запараллелить.

Эта мысль является центральной темой книги «The Mythical Man Month».

Читайте также:


Закон Конвея

Закон Конвея в Википедии

Этот закон предполагает, что технические рамки системы будут отражать структуру организации. Обычно его упоминают в контексте улучшения организации. В законе Конвея говорится, что, если организация разделена на небольшие отдельные команды, то и программное обеспечение будет разделено подобным образом. Если организация выстроена вокруг «вертикалей», которые ориентированы на улучшение и сервис, то система программного обеспечения будет отражать это.

Читайте также:


Закон Каннингема

Закон Каннингема в Википедии

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

Закон Каннингема можно также рассматривать как эквивалент французской поговорки «prêcher le faux pour savoir le vrai» (буквально «лгать, чтобы выяснить правду»). Известно, что Шерлок Холмс иногда использовал этот принцип (например, в «Знаке четырёх»). В комиксе xkcd «Зов долга» (Duty Calls) использована схожая концепция.

Читайте также:


Число Данбара

Число Данбара в Википедии

Число Данбара — ограничение на количество постоянных социальных связей, которые человек может поддерживать. О каждом человеке, включённом в это число связей, вы точно можете сказать, кто это и как он связан с другими людьми. Есть разногласия с точным числом.

Данбар предполагал, что человек может комфортно поддерживать только 150 стабильных связей. Он описал жизненную ситуацию, которая поможет определить число таких связей в вашей жизни: количество людей, которые не смутят вас своим появлением и кому вы будете рады в качестве собутыльника, если случайно столкнётесь в баре. Это число будет лежать где-то между 100 и 250.

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

Читайте также:

Бритва Хэнлона

Бритва Хэнлона в Википедии

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

Роберт Джей Хэнлон

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


Закон Хофштадтера

Закон Хофштадтера в Википедии

Любое дело всегда длится дольше, чем ожидается, даже если учесть закон Хофштадтера.

Дуглас Хофштадтер

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

Из книги «Gödel, Escher, Bach: An Eternal Golden Braid».

Читайте также:


Цикл хайпа и закон Амара

Цикл хайпа в Википедии

Мы склонны переоценивать эффект от технологии в краткосрочной перспективе и недооценивать эффект в долгосрочной перспективе.

Рой Амара

Цикл хайпа является визуализацией кривых волнения и развития технологии во времени. Впервые был представлен компанией Gartner. Лучше показать на примере:

Цикл хайпа

(Источник изображения: авторство Jeremykemp, взято из Английской Википедии, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=10547051)

Если коротко, этот цикл показывает, что вокруг новой технологии обычно наблюдается взрыв ажиотажа относительно её потенциального воздействия. Команды часто поспешно прыгают с головой в эту новую технологию, но в итоге разочаровываются результатом. Это может быть связано с тем, что технология ещё недостаточно развита или приложения в реальном мире ещё не до конца реализованы. По прошествии времени возможности технологии возрастают, и практическая польза от неё увеличивается. Что позволяет командам продуктивно использовать эту технологию. Рой Амара сформулировал это наиболее ёмко: «Мы склонны переоценивать эффект от технологии в краткосрочной перспективе и недооценивать эффект в долгосрочной перспективе».


Закон Хайрама (Закон неявных интерфейсов)

Закон Хайрама онлайн

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

Хайрам Райт

Закон Хайрама гласит, что, когда у вас есть достаточно большое количество пользователей API, любое действия этого API (даже неопределённые в рамках публичной документации) в конечном итоге повлияют на кого-то. Тривиальный пример: нефункциональный элемент, такой, как время ответа API. Менее значительный пример: пользователи, которые опираются на использование регулярных выражений при определении типа ошибки API. Даже если публичная документация API не говорит ничего о тексте сообщения ошибки, явно указывая, что нужно смотреть на код ошибки, некоторые пользователи могут использовать текст сообщения, и изменение этого текста приводит к поломке API у таких юзеров.

Читайте также:


Закон Мура

Закон Мура в Википедии

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

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


Закон Паркинсона

Закон Паркинсона в Википедии

Работа заполняет всё время, отпущенное на неё.

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

Если этот закон совместить с законом Хофштадтера, то картина окажется ещё более пессимистичной — работа заполнит всё отведённое на неё время и всё равно займёт больше времени, чем ожидалось.

Читайте также:


Закон Путта

Закон Путта в Википедии

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

Закон Путта часто сопровождается следствием Путта:

Каждая техническая иерархия со временем развивает инверсию компетенций.

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

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

Читайте также:


Закон сохранения сложности (закон Теслера)

Закон сохранения сложности в Википедии

Закон гласит, что в системе существует определённый уровень сложности, который невозможно уменьшить.

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

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


Закон негерметичных абстракций

Закон негерметичных абстракций в Википедии

Все нетривиальные абстракции, в какой-то степени, негерметичны.

Джоэл Спольски

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

Примером может служить процесс загрузки файла и чтение его содержимого. API файловой системы является абстракцией низкоуровневых систем ядра, которые, в свою очередь, являются абстракциями над физическими процессами изменения данных на диске (или флеш-памяти SSD). В большинстве случаев абстракция обработки файла в виде потока двоичных данных будет работать. Однако для магнитного накопителя последовательное чтение данных будет значительно быстрее чем рандомный доступ (из-за увеличения количества служебных ошибок). Но в случае с SSD-диском такие издержки отсутствуют. Для понимания этого примера потребуется разобраться с основами. Например, каталоги файлов в базе данных структурированы таким образом, чтобы снизить издержки при рандомном доступе. «Утечки» абстракций должны быть предусмотренным разработчиком при реализации.

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

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

Читайте также:

Реальный пример:


Закон тривиальности

Закон тривиальности в Википедии

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

В качестве примера приводится вымышленный комитет, работа которого заключалась в согласовании проекта атомной электростанции. Члены комитета проводят большую часть своего времени за обсуждением структуры велосипедного навеса, а не гораздо более важного проекта самой электростанции. Бывает трудно внести ценный вклад в дискуссию на очень большие и сложные темы без высокой степени предметной экспертизы или подготовки. Тем не менее, люди хотят вносить ценный вклад. Отсюда возникает тенденция уделять слишком много времени мелким деталям, которые легко обосновываются, но не обязательно имеют особое значение.

Вымышленный пример, рассмотренный выше, привел к использованию термина «Bike Shedding» (если переводить дословно, то получится что-то вроде «сарая для велосипедов») в качестве выражения для траты времени на тривиальные детали.


Философия Unix

Философия Unix в Википедии

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

Современные практики, такие как «Архитектура Микросервисов», могут рассматриваться как применение этого закона. Сервисы небольшие, сфокусированы на одной специфичной задаче, что позволяет создать сложное поведение путём составления простых строительных блоков.


Модель Спотифай

Модель Спотифай в Википедии

Модель Спотифай — подход к организации команды и структуре компании, которая была популяризирована компанией-разработчиком Spotify. В этой модели команды организованы вокруг функций, а не технологий.

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

Модель Спотифай

Читайте также:


Закон Вадлера

Закон Вадлера на wiki.haskell.org

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

  1. Семантика
  2. Синтаксис
  3. Лексический синтаксис
  4. Лексический синтаксис комментариев

(Короче говоря, на каждый час, потраченный на семантику, придётся 8 часов обсуждения синтаксиса комментариев).

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

Читайте также:


Принципы

Принципы больше похожи на гайдлайны для дизайна системы.

Принцип Парето (Правило 80/20)

Принцип Парето в Википедии

Большинство вещей в жизни распределяются неравномерно.

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

В 1940-х американо-румынский инженер доктор Джозеф Юран, которому приписывают создание контроля качества, начал применять принцип Парето в вопросах качества.

Этот принцип также известен как правило 80/20.

Примеры из реальной жизни:


Принцип устойчивости (Закон Постела)

Принцип устойчивости в Википедии

Будьте консервативны в том, что вы делаете и либеральны в том, что принимаете от других.

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

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


SOLID

Это акроним, который расшифровывается следующим образом:

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

Принцип единственной ответственности

Принцип единственной ответственности в Википедии

Каждый модуль или класс должен иметь одну единственную ответственность.

Первый из пяти принципов SOLID. Этот принцип гласит, что модуль или класс должен делать всего одну вещь. В практическом смысле это означает, что одно маленькое изменение при доработке программы должно требовать изменения только в одном компоненте. Например, изменение в механизме проверки сложности пароля должно потребовать изменения только в одной части программы.

Теоретически это должно делать код более надёжным и простым для изменений. Знание, что изменённый компонент несёт на себе единственную ответственность, означает, что тестирование этого изменения будет простым. Возвращаясь к предыдущему примеру, изменения в компоненте проверки сложности пароля должны повлиять только на часть программы, отвечающую за проверку пароля. Гораздо сложнее рассуждать о влиянии изменения в компоненте, у которого сразу несколько функций.

Читайте также:


Принцип открытости/закрытости

Принцип открытости/закрытости в Википедии

Сущности должны быть открыты для расширения, но закрыты для изменения.

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

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

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

Читайте также:


Принцип подстановки Барбары Лисков

Принцип подстановки Барбары Лисков в Википедии

Должна быть возможность заменить тип на подтип без поломки системы.

(от редактора)

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

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

В качестве примера представьте, что у нас есть метод, который читает XML-документ из файла. Если метод использует в качестве основы тип ‘file’, то мы должны иметь возможность использовать в функции и любое производное от ‘file’. Если ‘file’ поддерживает поиск в обратном порядке, а парсер XML использует эту возможность, и при этом подтип ‘network file’ выдаёт ошибку при попытке поиска в обратном порядке, тогда подтип ‘network file’ нарушает описываемый принцип.

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

Читайте также:


Принцип разделения интерфейса

Принцип разделения интерфейса в Википедии

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

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

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

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

Читайте также:


Принцип инверсии зависимостей

Принцип инверсии зависимостей в Википедии

Высокоуровневые модули не должны зависеть от низкоуровневой реализации.

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

В качестве примера представьте, что у нас есть программа, которая считывает мета-данные с сайта. Мы предполагаем, что главный компонент должен знать о компоненте, занимающимся скачиванием контента с сайта, а затем и о компоненте, считывающем мета-данные. Если мы примем во внимание инверсию зависимостей, то основной компонент будет зависеть только от абстрактного компонента, который может извлекать байтовые данные, а затем от абстрактного компонента, который мог бы считывать метаданные из байтового потока. Основной компонент не будет знать о TCP/IP, HTTP, HTML и прочем.

Этот принцип сложный. Может показаться, что он «инвертирует» вероятные зависимости системы (отсюда и название). На практике это также означает, что отдельный управляющий компонент должен гарантировать, что используются правильные реализации абстрактных типов (например, в предыдущем примере нечто должно по-прежнему предоставлять компоненту чтения метаданных загрузчик файлов HTTP и средство чтения метатегов HTML). Это также касается таких шаблонов, как Инверсия управления и Внедрение зависимости.

Читайте также:


Принцип DRY

Принцип DRY в Википедии

Каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в рамках системы.

DRY это акроним от фразы Don’t Repeat Yourself («Не повторяй себя»). Этот принцип призван помочь разработчикам избежать повторений в коде и хранить информацию в одном месте. Был впервые описан в 1999 году в книге The Pragmatic Developer Эндрю Ханта и Дейва Томаса.

Принципу DRY полностью противоположен принцип WET — Write Everything Twice или We Enjoy Typing (Пиши всё дважды или Мы любим печатать).

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

Читайте также:


Принцип YAGNI

Принцип YAGNI в Википедии

Акроним You Aren’t Gonna Need It (англ. Вам это не понадобится).

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

(Рон Джеффриз) (Соавтор методологии экстремального программирования и автор книги “Extreme Programming Installed”)

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

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

Читайте также:


Принцип KISS

Принцип KISS в Википедии

KISS это акроним от фразы Keep it simple, stupid («Сохраняй простоту») или Keep it stupid simple («Сохраняй вещи до глупого простыми»). Принцип KISS утверждает, что большинство систем работают лучше всего, если они остаются простыми, а не усложняются. Поэтому в области проектирования простота должна быть одной из ключевых целей, и следует избегать ненужной сложности.

На практике:

Читайте также:


Список литературы

Если вас заинтересовали перечисленные концепции, то вам могут понравиться следующие материалы:

TODO

Привет! Если вы это читаете, то вы перешли по ссылке на статью, которая ещё не написана. Простите за это! Я работаю над этим.

Не стесняйтесь заводить issue с пожеланиями или присылайте Pull Request со своими правками или новыми темами.


Они внесли свой вклад

Большое спасибо этим прекрасным людям (что значат emoji?):

Alexandr Kizilow
Alexandr Kizilow

🖋
Natalia Ryzhova
Natalia Ryzhova

🖋
Anastasia Lopatina
Anastasia Lopatina

🖋
Nikita Slimov
Nikita Slimov

🖋
Realetive
Realetive

🖋
Ivan Prodaiko
Ivan Prodaiko

🖋