Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Про именование интерфейсов #27

Open
sokolovvs opened this issue Nov 24, 2020 · 45 comments
Open

Про именование интерфейсов #27

sokolovvs opened this issue Nov 24, 2020 · 45 comments

Comments

@sokolovvs
Copy link

Здравствуйте, Адель!
Привожу цитату из книги:

Второй вариант: использование суффикса *Interface, например, создать интерфейс StorageInterface. Таким образом, имея класс Storage реализующий интерфейс StorageInterface, мы постулируем, что у нас есть интерфейс и его «главная» реализация. Все остальные реализации этого интерфейса выглядят вторичными. Само существование интерфейса StorageInterface выглядит искусственным: он был создан, чтобы код удовлетворял каким-то принципам, или просто для юнит-тестирования.

Это явление встречается и в других языках. В C# принят префикс I*. Интерфейс IList и класс List, например. В Java не приняты префиксы и суффиксы, но там часто случается такое:

class StorageImpl implements Storage

https://github.com/adelf/acwa_book_ru/blame/master/manuscript/2-di.md#L362

Вероятно здесь речь именно про то, что имя класса Storage плохое только если он реализует интерфейс с названием StorageInterface, т.к.

мы постулируем, что у нас есть интерфейс и его «главная» реализация

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

@adelf
Copy link
Owner

adelf commented Nov 25, 2020

PSR этот стандарт рекомендует только для PSR. А не для кода. Т.е. эта рекомендация не входит в PSR-1 или PSR-12. Видимо, там таки нашлись здравомыслящие люди, которые сумели отговорить PHPFIG эту спорную идею выносить как стандарт для всех.

Storage - неплохое. Ну может FileStorage чуть поточнее будет. Это хороший пример класса, которые в начале развития проекта, скорее всего будет одним на весь проект, а потом необходимо будет интерфейс FileStorage и несколько его реализаций: LocalFileStorage, S3FileStorage и т.д.

FileStorage - отличное имя. Смотришь на него и всё отлично. Необходимая информация передана за маленькое количество букв. Ежу понятно, что это хранилище файлов. Причем, что именно за хранилище - непонятно и это тоже хорошо. Это абстракция :) Ну вот представь, у тебя в проекте сотни раз использовано наименование класса или интерфейса, которое отвечает за хранение файлов. Что ты предпочтешь? FileStorage? Или FileStorageInterface, FileStorageContract? FileStorageClass? Разумеется первое. Лет 6-7 назад я тоже думал, что очень важно знать интерфейс это или класс мы сейчас хотим использовать? Но посколько везде используется DI и мы не должны делать сами какое нибудь new FileStorage(), то нам вообще всё равно что стоит за этим именем.

НУ и напоследок банально рассмотрим ситуацию где интерфейс и реализация.
Что лучше: interface FileStorageInterface. class FileStorage implements FileStorageInterface и еще сотни FileStorageInterface в проекте
Или interface FileStorage. class LocalFileStorage implements FileStorage и еще сотни FileStorage в проекте?

Простое название FileStorage мотивирует давать говорящие имена классам, а не просто тупое Имя интерфейса без суффикса. Это тоже полезно.

@6562680
Copy link

6562680 commented Jan 7, 2022

На мой взгляд, всё несколько проще.
Интерфейс сегрегация есть такое слово.

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

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

Это позволяет вам потом создать общий контракт соединив мелкие в один (множественное наследование интерефейсов в PHP отлично работает, а классов можно сделать трейтами, но это надо делать только там, где иначе - невозможно).

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

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

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

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

Возвращаясь к предмету дискуссии - StorageInterface подразумевает, что сюда можно всунуть абсолютно любое хранилище чего-либо. Это не плохо опять же, но это слишком гигантский и всеобъемлющий контракт! Чтобы написать настолько стандартизированную инструкцию, чтобы сохранять что угодно куда угодно при помощи двух кнопок можно крышей уехать. Поэтому верно - разумнее сделать FileStorage класс, который хоть немножко уменьшает обобщение, и говорит о том, что это хотя бы хранилище файлов, а еще лучше FilesystemStorage, которая уже говорит что это хранилище на вашем жестком диске. И суффикс "Interface" к нему добавится тогда когда у вас появится AudioFilesystemStorage, VideoFilesystemStorage, ImageFilesystemStorage, AudioAwsStorage, VideoAwsStorage, ImageAwsStorage. В этом случае FilesystemStorageInterface (не важно что) ограничит хранение на локальном диске, а AudioStorageInterface ограничит хранение только для аудио (не важно куда)

Сделаю маленькую ремарочку на тему "супер интерфейса" - есть такая вещь как Psr\Cache. Так вот все мировое сообщество ПХПшников 20 лет пытается стандартизировать кеширование, и придумало раза с 20го только CacheItemPoolInterface, и то в нем нет гарантии, что поддерживаются теги. То есть суперпулю пытались делать, но поняли, что лучше ее наворачивать по ходу, выбрасывая постоянно повторяющиеся штуки в стандарты, а не садиться и ордой вокруг круглого стола стандарт писать.

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

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

Собственно если взять Симфони - они активно используют "алиасы", которые в других фреймворках обозвали (и обложили) сервис локаторами (а сами тут же сервис локаторами назвали "умные факторки" и вообще все напутали). Чтобы не спамить большими интерфейсами предлагается просто давать некоему обьекту текстовое имя типа "app.serializer" после чего в конфиге ВРУЧНУЮ его пропихивать в обьект (т.е. автоподстановка не будет работать, а будет ручная, которая быстрее, но которую дольше менять в коде и помнить о ней).

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

@DplusG
Copy link

DplusG commented Feb 28, 2024

Конкретно от слова Interface можно и отказаться, как от венгерской нотации.
Хотя ничего криминального не вижу, и дело привычки. Как и Interface|Contract
Причем в куче мест вы скорее всего встретите приписку Interface как в том же PSR.

"просто для юнит-тестирования" - как-то обесценивает :)

А от других приписок уже не получится отказаться. Например, Contact::class.
Что перед нами? Controller, Enum, Factory, Request, Model, Repository, Service?
Вот и получается потом что-то вроде такого:
ContactController,
ContactRoleEnum,
ContactFactory,
AddContactRequest,
ContactModel,
ContactRepository,
ContactService

Да, они указывают на слой. Но определенное представление о типе также появляется. По наименьшему удивлению вы не ожидаете увидеть в ContactRoleEnum экшен, а скорее всего какой-то набор key,value список

@6562680
Copy link

6562680 commented Feb 28, 2024

Точно!

Однако я предпочитаю не лениться и писать Interface в конце и Abstract в начале, ибо интерфейс хорошо заметен в use блоке, а Abstract отлично сортируется по алфавиту, и все абстракции сразу оказываются выше деталей.

Я не смог найти вас в телеграм, давайте поддерживать связь, спасибо!

@adelf
Copy link
Owner

adelf commented Feb 28, 2024

А от других приписок уже не получится отказаться. Например, Contact::class.
Что перед нами? Controller, Enum, Factory, Request, Model, Repository, Service?

Иногда делают исключение для какого-то одного типа и обычно классы сущностей называют без суффикса. Просто Contact.

все абстракции сразу оказываются выше деталей

И все-таки. Зачем знать в конкретном месте, если мы используем таки DI, интерфейс это или класс(абстрактный или нет)?

@6562680
Copy link

6562680 commented Feb 28, 2024

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

Вообще на днях пытался задвинуть errorBag в php-internals. Обгадили. Сказали "так нельзя делать, ужас!", но блин, я не видел еще в своей жизни тимлида который готов сесть и с тобой диаграмму методов по солиду раскладывать. Ставят задачу в которой метод сначала здоровенный и гигантский, и сильно удобнее ошибки собирать глобально по скрипту, и потом в апи их выплюнуть, чем дробить на микрометоды и эксепшены лютую интеграцию методов на 400 (которая по солиду станет тысячи три методов)

https://github.com/6562680/error-bag

@DplusG
Copy link

DplusG commented Feb 29, 2024

обычно классы сущностей называют без суффикса

Согласен, увлекся) но можем еще и ContactList|ContactCollection получить. Это больше как иллюстрация, что не все суффиксы получится убрать

@adelf
Copy link
Owner

adelf commented Feb 29, 2024

@DplusG Я не против суффиксов. Они хороши. Практически все приведенные тобой. Я против конкретно *Interface, *Contract, и прочей ерунды)

@6562680
Copy link

6562680 commented Feb 29, 2024

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

@adelf
Copy link
Owner

adelf commented Feb 29, 2024

@6562680 если стараться нормально называть, то проблем быть не должно. Например LaravelConfig implements Config. CloudFileStorage implements FileStorage.

@DplusG
Copy link

DplusG commented Feb 29, 2024

https://github.com/6562680/error-bag

Попробовал въехать в твое предложение и, честно говоря, все равно не понял почему try-catch не вариант. Ну или ***Response, который нужное в себе содержит

метод сначала здоровенный и гигантский

Тут уже наводит на мысли, что на методе слишком много ответственности и error-bag будет лечением последствий, а не причин

@6562680
Copy link

6562680 commented Feb 29, 2024

ну как не вариант, это не альтернатива, это дополнение к try/catch

try/catch разруливает простые функции, и вешает прогу.
сборка ошибок не вешает прогу, не должна вешать.
и потом когда ты new Exception() вызываешь собирается трейс.
если в исходном массиве для batch у тебя есть ошибочные варианты, которые ты сначала бросаешь, а потом ловишь - ты дважды теряешь время на сбор трейсов и отлов.

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

то самое понятие "что-то пошло не так" - которое должно сказать тебе, что код надо менять.

если все прошло правильно, но выпало исключение - то оно должно стать warning, а значит не было смысла собирать трейс для него, все равно на вывод пойдет скорее всего ->getMessage(), а трейс собирается не когда ты попросил, а когда ты вызвал new \Exception().

простейший пример где исключения дают "приехали" - это массовая работа с чем-либо.
берешь с очереди или в кроне 10 записей. при этом 5 из них повторяются. то есть отправить в интеграцию/api можно (и нужно) только 5. т.е. в худшем случае "исключений" будет 5. а в исходной бд с очередью куда надо логи положить - ячеек 10. надо откопировать, форматировать, в массивы положить. всё это пишется на форичах обычно. и в добавок возвращается из функции.

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

а если не поймаешь - то схлопнется логика, которая должна была продолжиться.

вот почему ошибки это не исключения.

@6562680
Copy link

6562680 commented Feb 29, 2024

метод сначала здоровенный и гигантский

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

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

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

так вместо этого мировое сообщество решило упороть людей в O log N анализ скорости выполнения функций (смотри все эти идиотские mock-собесы), который в общем-то очень нужен, но опять же - для идеального мира, и если по правилу парето - то для тех самых 20 случаев из 100, на которых нужно много работы и которые сработают почти никогда, но могут.

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

@DplusG
Copy link

DplusG commented Feb 29, 2024

@6562680, а ошибки на уровне записи в БД или соответствие типам или еще что-то?

Мне пока видится, что должно это решаться в userland:

  1. Обход списка, валидация каждого элемента. При ошибках не бросать исключения, чтобы трейсы не собирать.
  2. После этого что-то вроде $list->getErrors()
  3. Дальше уже либо выбрасывать исключение/response в таких-то элементах у вас проблемы. В зависимости от того, записывать надо пачкой или только валидные.
    Если пачкой - можно и exception.
    Если валидные - {message: "записано 3 из 10"}

@6562680
Copy link

6562680 commented Feb 29, 2024

@DplusG

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

из базы все вытащил. оно легло туда валидное и ты все проверил. шлешь его на внешний сервис. внешний сервис вернул ошибки, хотя с твоей стороны все валидно. запросы надо повторить или пометить статус операции в fail, начать откат. т.е. в вышеописанном случае возьмем худший сценарий - было 10 телефонов, послал 5 (ибо повторы) и все 5 вернули ошибку. нужно 10 операций пометить что они FAIL, а не 5, хотя внешняя интеграция ошиблась 5 раз.

безусловно приятно, что ошибки ломают прогу и триггерят тебе на почту, что код на той стороне теперь косячит (что-то могли поменять) и тебе надо довести свой до ума. но это поломает тебе сайт... теперь твои юзеры столкнулись с проблемой, когда они видят 500 ошибку там, где ты вообще ничего не трогал. с точки зрения клиента лучше бы они видели "операция не удалась" и пинговали поддержку, чем прога мертво начала падать в 500, потому что теперь там исключение.

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

ты совершенно правильно говоришь насчет валидации. единственное терпимое решение, которое УМЕНЬШАЕТ (а не РЕШАЕТ) количество проблем до минимума - это валидация данных как можно раньше. Но внешний сервис все равно может вернуть рантайм ошибки, которые можно итерационно допилить в валидации конечно, но хотелось бы, чтобы они сразу были ошибками без триггера 500-ки.

т.е. тру подход это сначала раздробить код на микрометоды по 1-2-3 строки, затем валидацию поближе к месту начала задачи, но это все сильно увеличивает и количество кода и вероятность 500 там где не должно. это хорошо и правильно с точки зрения надежности, но требует каждый раз переделки концептуально. а мы то целимся в то, чтобы старый код не трогать (стараться не трогать), если что-то создает причины для изменения, то оно верно с научной точки зрения (той самой которая повышает тебе ЧСВ), но неверно с бизнесовой (с той самой, которая заставляет тебя срочно выйти на работу в выходной)

@DplusG
Copy link

DplusG commented Feb 29, 2024

бизнес хочет быстро...с точки зрения продукта он прав

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

  • быстро сейчас может и ошибок увеличить, а это негатив от клиентов

@DplusG
Copy link

DplusG commented Feb 29, 2024

@6562680 , а что за мок-собесы. Впервые слышу)

@6562680
Copy link

6562680 commented Feb 29, 2024

бизнес хочет быстро...с точки зрения продукта он прав

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

  • быстро сейчас может и ошибок увеличить, а это негатив от клиентов

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

@6562680
Copy link

6562680 commented Feb 29, 2024

@6562680 , а что за мок-собесы. Впервые слышу)

та тут временами в ютубе начали выпадать блоггеры которые собеседуют инженеров амазона, гугла и яндекса, и дают там задачу по типу "есть массив [1,3], [4,7], [2,8], представьте что это промежутки. верните непересекающиеся промежутки" и показывают "как надо правильно проходить собесы" - дескать пообщаться с заказчиком, узнать детали, предложить несколько решений, снова пообщаться, и потом продемонстрировать умение писать код который работает за log N вместо N в квадрате.

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

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

@DplusG
Copy link

DplusG commented Feb 29, 2024

Понятно, ну это большие компании. Для них это актуально

@DplusG
Copy link

DplusG commented Feb 29, 2024

@6562680

было 10 телефонов, послал 5 (ибо повторы) и все 5 вернули ошибку. нужно 10 операций пометить что они FAIL, а не 5, хотя внешняя интеграция ошиблась 5 раз

Операций, вероятно, тоже 5, по количеству запросов?
А как 10 телефонов попадает с повторами и как они схлопываются?
Можно добавить проверку на присутствие телефона в списке и исключить повторы?

Мы ведь можем не проверять 2й запрос к сервису, если он по таймауту вылетел?

@6562680
Copy link

6562680 commented Feb 29, 2024

@6562680

было 10 телефонов, послал 5 (ибо повторы) и все 5 вернули ошибку. нужно 10 операций пометить что они FAIL, а не 5, хотя внешняя интеграция ошиблась 5 раз

Операций, вероятно, тоже 5, по количеству запросов? А как 10 телефонов попадает с повторами и как они схлопываются? Можно добавить проверку на присутствие телефона в списке и исключить повторы?

Мы ведь можем не проверять 2й запрос к сервису, если он по таймауту вылетел?

я допустим в очередь кладу все команды пользователя. если он дважды залогинился и кладу в очередь две операции логина, которые уже потом могут привести к отсылке двух писем или одного. вот почему в базу могли попасть копии. они отличаются временем. тогда как сервис звонилки часто умеет работать с пачками и мне разумнее если письмо одинаковое сделать reduce (подход map-reduce не устарел ни разу) и отправить только 5 телефонов, а в очередь записать логи, будто было сделано 10 задач.

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

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

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

вот почему очередь должна работать по двум критериям - по буфферу штук и по времени если буффер не накопился. типичный же ненастроенный рэббит будет работать как буффер = 1, т.е. получая задачу немедленно её выполнять.

@DplusG
Copy link

DplusG commented Feb 29, 2024

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

Смысл идти по всем 5 и собирать список ошибок, если они все скорее всего будут "сервер лежит/изменил контракт"
Данные-то верны, мы их уже проверили

@6562680
Copy link

6562680 commented Feb 29, 2024

ps. самое главное открытие - это то что асинхронка яваскрипта например заключается в том, чтобы выбрасывать пачку задач в очередь и в конце кода запускать while true. начиная с понимания этого приходит понимание, что если что-то нужно делать параллельно, то атомарность операции это не про "работу с одной записью", а про разбитие по перпендикулярной оси. т.е. одна операция для 10 записей вместо 10 операций для одной записи.

@6562680
Copy link

6562680 commented Feb 29, 2024

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

Смысл идти по всем 5 и собирать список ошибок, если они все скорее всего будут "сервер лежит/изменил контракт" Данные-то верны, мы их уже проверили

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

@DplusG
Copy link

DplusG commented Feb 29, 2024

А так бывает в твоей системе "сломалось у Васи, но не сломалось у Пети"?

@6562680
Copy link

6562680 commented Feb 29, 2024

Ну Вася отослал телефон без семерки (и для твоей системы это норм, она вообще телефоны сохраняет в исходном виде, потому что их приводить к правильным - рискованно, можно позвонить не тому человеку, или недопозвонить тому, кому надо), а Петя молодец и отослал в формате href="tel:". В итоге внешняя система Вася ответила дулю, а Петя - все хорошо.

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

То есть здесь снова не выбор между "как лучше" - оба варианта нужны. Например цепочка команд Васи упадет если первая команда Васи сломалась. Но вот операции Пети упасть из-за Васи не должны.

@DplusG
Copy link

DplusG commented Feb 29, 2024

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

@6562680
Copy link

6562680 commented Feb 29, 2024

Тот самый принцип CQRS, запросы можно параллелить, а вот команды - только пайплайнить (цепочить). И если один из этапов пайплайна лег - хорошо если не надо делать откат, но остановить надо все. А вот если не цепочка упала, а параллель, тут надо игнорить и идти дальше.

@6562680
Copy link

6562680 commented Feb 29, 2024

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

Я уже проходил через проверку телефонов за свою практику. Ничем хорошим не заканчивается. Гугловской либой максимум преобразовать и, если не удалось, сохранить в одно поле NULL, а во второе - исходное значение. Но вот есть системы типа Mindbox, которую сделали русские индусы, которая предпочитает уникализировать телефоны и если есть повтор - угонять у одного чела и передавать телефон другому, отталкиваясь от того, что "раз я прислал, значит наверное я все проверил". и потом те кто нужно рассылки не получают, а маркеты сьедают мозг.

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

@DplusG
Copy link

DplusG commented Feb 29, 2024

То есть запросить у пользователя правильный формат в форме и подтверждение телефона не получится? Печаль

@6562680
Copy link

6562680 commented Feb 29, 2024

это очень правильный подход всунуть туда хоч бы jquery или что-то поумнее, чтобы сразу прилетел каноничный телефон со страной. это супер вариант.

движей несколько:

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

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

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

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

@DplusG
Copy link

DplusG commented Feb 29, 2024

"Почему такой бедный если такой умный" ну в такой риторике "потому что не устраивает неэквивалентный обмен" :)
В общем, ваша фича потому и не принялась, потому как в итоге уперлась в некомпетентный менеджмент, который против поставить маску на инпуте

@6562680
Copy link

6562680 commented Feb 29, 2024

"Почему такой бедный если такой умный" ну в такой риторике "потому что не устраивает неэквивалентный обмен" :) В общем, ваша фича потому и не принялась, потому как в итоге уперлась в некомпетентный менеджмент, который против поставить маску на инпуте

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

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

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

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

@DplusG
Copy link

DplusG commented Feb 29, 2024

сайт не требует его переделывания, а вы пытаетесь развести нас на бабки

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

юзер устает писать "правильно"

Это же не ваша хотелка, а общепринятая практика

@6562680
Copy link

6562680 commented Feb 29, 2024

сайт не требует его переделывания, а вы пытаетесь развести нас на бабки

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

юзер устает писать "правильно"

Это же не ваша хотелка, а общепринятая практика

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

@DplusG
Copy link

DplusG commented Feb 29, 2024

но я других не видел)

А вы ведь уже 11 лет работаете? Попробуйте сходить на собесы, только где не просят mock-тесты )
Может еще и по зарплате выйграете

@6562680
Copy link

6562680 commented Feb 29, 2024

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

но я других не видел)

А вы ведь уже 11 лет работаете? Попробуйте сходить на собесы, только где не просят mock-тесты ) Может еще и по зарплате выйграете

Да, после того как вернулся с тюрьмы за протесты 20го года в Беларуси, по собесам ходил полгода. Общее ощущение - компании сошли с ума. Но как сказал тот же атлант который про "умный и бедный" - если все сошли с ума, то может это вы сошли с ума?

@6562680
Copy link

6562680 commented Feb 29, 2024

Да, именно 11 лет. И в каждой конторе лид всегда такой же (а это Брест, Киев, Минск, даже немецкий Бонн (там особенно жестко упор на "ты можешь сделать или тебя уволить?")). Встречал на мелких бизнесах умных лидов, которые заставляют бизнес платить много за то, что те шарят и выделять месяц на задачу "подключите очередь на проект" (сейчас это задача дня, не можешь настроить раббит - сделай крон и таблицу в бд, разница будет видна только если клиентов число завалит за миллион, но вы уже станете миллионерами к этому времени)

Но там другое. Дрючат тем, что в репозитории пробелы, или тем что код не по солиду написан. Ты молчишь и правишь. А в определенный момент тебе задвигается, что ты всё делаешь слишком долго, потому что он гений, а ты нет. Чессговоря интересный выбор между двух зол.

Либо бизнес не хочет беседовать в ключе науки, либо наука ставит вопрос "кто из вас умнее", и вторую решить сильно сложнее (но возможно, в отличие от первой), ведь принять что "оба правы" умный человек часто не в силах. Начинается поиск мудрости. Которая будет найдена именно там, где науку игнорируют. Т.е. там где по наитию считается, что каждый мастер и поэтому прав, а значит спорить бессмысленно и надо просто делать. Не можете договориться? Лесом обоих.

@DplusG
Copy link

DplusG commented Feb 29, 2024

Аа, вот вы о чем.

я то понимаю почему он это делает

Экономит деньги бизнесу или для чего?

@6562680
Copy link

6562680 commented Feb 29, 2024

Аа, вот вы о чем.

я то понимаю почему он это делает

Экономит деньги бизнесу или для чего?

Под этим флагом (экономия денег бизнеса) он "просто выбирает себя". Как и все люди, которым жизнь дорога. То есть он на краю и знает, что вперед нельзя. И бизнес так сделал, чтобы он был на краю. И когда тот пытался нанять себе помощника - выяснилось, что тот не шарит и поэтому не нужен. Еще бы, двое против генерального сильно опаснее, чем один.

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

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

@DplusG
Copy link

DplusG commented Feb 29, 2024

¯_(ツ)_/¯
не знаю, что вам посоветовать)
наверно вы все правильно делаете и вам нужно найти отдушину в каком-нибудь опен-сорс проекте

@6562680
Copy link

6562680 commented Feb 29, 2024

¯_(ツ)_/¯ не знаю, что вам посоветовать) наверно вы все правильно делаете и вам нужно найти отдушину в каком-нибудь опен-сорс проекте

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

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

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

@DplusG
Copy link

DplusG commented May 17, 2024

Так-с. Мне тут по теме появилось что добавить, может кому полезно будет))
Обычно никто не заморачивается и раскладывает код без правильного деления, ни горизонтального ни вертикального.

А когда вы начинаете так делать, то все интерфейсы у вас переезжают по своим уровням.
В доменном слое SomeRepositoryInterface выглядит излишним. Логичнее читается SomeRepository или SomeCollection (конкретная реализация-то уезжает вообще в другую папку. Теперь ясна причина, хоть она и в скобках, кажется, это ключевой момент)
В сервисном слое аналогично, например, LoggerInterface вообще не добавляет читабельности перед Logger.

Теперь полностью согласен с Аделем

@6562680
Copy link

6562680 commented May 17, 2024

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

Интерфейсы юзаю для трех вещей:

  1. имя в контейнере зависимостей, если реализация требует настроек перед запуском. Т.е. просто логгер нет смысла биндить, так это "логгер для чего-то", хотя пример хреновый
  2. это тип на входе при строгой типизации которая пропустит ЭТО и любое ТАКОЕ ЖЕ как это
  3. и это набор поведений, если писать пары интерфейс-трейт. в интерфейсе пишем CanDriveInterface, рядом CanDriveTrait, классу цепляем оба и теперь класс может проходить в те места, где другим нельзя

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

Поэтому я сильно обрадовался когда наконец понял в чем смысл "неблокирующих" функций.
И что такое программирование вида

doSomething(Input $input, Context $context)
doSomethingAfterSomething(Input $input, Context $context)
doSomethingAfterSomethingAfterSomething(Input $input, Context $context)

И в третьем этапе при таком подходе всегда можно одним ифом проверить был ли сделан первый. При этом после того как фича написана, можно подменить Input и Context на типизированные версии их же.

===

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants