Pull to refresh
13
0
Евгений @flr

Co-Founder/CTO at Roistat t.me/codeconv

Send message
Спасибо, там действительно было двусмысленно написано. У нас глаз на такие вещи замылен. То что для нас очевидно, совсем не очевидно внешнему читателю. Поправил описание модели. Не идеально, но хотя бы пока что людей не смущать. Если будут варианты лучше — предлагайте.
Я полностью согласен с тем, что это важный вопрос, и я также заинтересован в том, чтобы Code Conv улучшался. Это одна из целей, зачем он выложен в Open Source.

Спасибо за развернутое пояснение своей позиции. Мы это обдумаем и возможно сделаем какие-то доработки в правиле.
Вы или ваш коллега прямо пишет что «y объектов с данными не может быть практически ничего кроме геттеров».

По приведенной ссылке общение идёт вообще по другому вопросу (там речь о том, что нужно опускать слово get у геттеров). В коде «$user->registrationDate()» метод «registrationDate()» не может быть ничем иным, кроме как геттером. То есть фраза вырвана из контекста.

Но в любом случае спасибо за отзыв. Это всегда полезно.
Возможно я неправильно описал, как это происходит у нас. Мы знаем, в какой зоне была сохранена. У пользователя в профиле установлена зона и мы с ней и работаем. То есть мы не «отбрасываем» зону, мы знаем какие зоны у пользователя были в какое время. Просто мы не дублируем её в каждой колонке с датой в каждой записи в БД. Никаких последствий в долгосрочной перспективе я пока не увидел. Может, я что-то недопонял. :<
требуется чтобы в моделях не было логики, что явно противоречит GRASP. Как ни крути.

Явно противоречит… Как ни крути… Я так понимаю тут уже всё понятно и мне нет смысла пытаться объяснять, что каждый может интерпретировать слова Ответственность должна быть назначена тому, кто владеет максимумом необходимой информации для исполнения совершенно по-разному?

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

Модель является экспертом только в своих данных и манипуляциями с ними должна заниматься только она. Однако модель не является экспертом в других областях: она не может вызывать чужую бизнес-логику и даже не является экспертом в том, как сохранять себя в базу данных. То есть модель содержит сеттеры, которые правильно изменяют ее состояние. Но эти сеттеры не сохраняют модель в бд, они не вызывают цепочку зависимых сервисов и т.п. Чтобы сохранить модель в бд, не требуется никаких особых знаний. Грубо говоря, все модели сохраняются одинаково. Это довольно абстрактные вещи, чтобы их вот так объяснить в комментарии, но основная суть моей позиции должна быть понятна.

В целом есть ощущение что наши уважаемые коллеги поспешили с публикацией черновика своих Code Conventions.

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

Хочу уточнить, как я понял, Вы считаете, что такой подход:… лучше чем: ...

Не совсем.

Во втором подходе в объекте User метод block() не содержит никакой другой логики, кроме как управления внутренним состоянием объекта. Для нас сеттеры и геттеры — это не обязательно отдать какое-то одно свойство или записать его, не делая никаких других операций с другими свойствами. Геттеры могут получать любые данные из внутреннего состояния объекта, а сеттеры — делать любые нужные манипуляции с ним. Это вскользь упоминается в Code Conv, но возможно стоит как-то более явно это написать. То есть второй вариант не нарушает нашего правила разделения логики.

Но, если мы во втором подходе вместо «more magic» добавим зависимость от каких-то внешних компонентов и внешней бизнес-логики, то это перестанет быть простым сеттером и станет тоже полноценной бизнес-логикой проекта. И тогда для нас это будет уже смешением данных и логики и нарушением Code Conv.

в первом случае кто-то может заблокировать пользователя в обход сервиса, или же написать отдельную «похожую» логику в другом месте


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

В обоих кейсах на самом деле есть проблема и неполноценное следование тому же GRASP. Например, во втором кейсе логика блокировки полностью завязана на объект User, в который эту логику вставили. То есть я не могу создать свой объект MyUser и воспользоваться существующей логикой. Логика строго превязана к конкретному объекту вместо того, чтобы работать с нужным ей интерфейсом и не требовать конкретной реализации. То есть данный код страдает «high coupling» из GRASP. В том числе это за собой тянет нарушение SRP и других принципов.
Да, бывает такое, что временная зона съезжает, как было в РФ. Но, опять же, проблема будет только в том случае, если человеку надо напомнить о чем-то именно в какой-то день единоразово и в какой-то момент времени суток и эта точка времени должна сдвигаться за сменой зоны (хоть пользователем, хоть правительством). У нас просто таких кейсов нет. А даже если появится, то Code Conv не запрещает сохранить в конкретном кейсе в нужном тебе формате. Это как в КОАП — обстоятельство непреодолимой силы. Если надо в какой-то задаче сохранить дату особым способом, то значит надо. А по умолчанию для 99.99% дат — нет.

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

Пользователь попросил напомнить ему о чем-то 1 июня 2018 года в 12:00 по Москве (UTC+03). Это определенная точка во времени, когда ему надо напомнить о событии. Если юзер переехал в другое место, то не факт, что точка должна смещаться за ним. Мы по-прежнему напомнить ему должны в 12:00 по Мск. А 12:00 UTC+03 навсегда останется одинаковым и будет равнозначно 09:00 UTC0. Если же в задаче описано так, что при перемещении пользователя данное событие должно тоже двигаться, то хранение зоны в БД нам никак не поможет, нам надо всё равно писать логику, обрабатывающую этот процесс перемещения.

Можешь подробнее написать какой-то пример конкретной задачи?
В целом с time() всё в порядке. Но есть приоритет. Для нас строка лучше числа. Просто удобнее дебажить, выводить пользователю, делать substr для выборки года/месяца/дня и т.д. В общем, удобнее в повседневном использовании. Но, если всё же нужно число, то используем его (правило это не запрещает). Просто на это требуется какая-то причина.
Вместо лишней конкатенации используем подстановку переменных в двойных кавычках

Спасибо, поправил. Там была речь про кавычки, так что никто не обращал внимание на геттер.)

Сервисы

А тут, кстати, не совсем то. У сервиса нет геттеров по определению. У сервиса просто методы, которые могут начинаться на get, но это не геттеры. Геттер в нашем документе — это метод, который возвращает какие-то данные из состояния объекта. А у сервисов состояний нет. Их методы, начинающиеся get, просто получают откуда-то какие-то данные.
Сначала хотел написать, что (для меня) значат в данном случае Coupling и Cohesion, но понял, что это скорее потянет на отдельную статью. Так что на такую абстрактную формулировку могу ответить только так же абстрактно: нет, с моей точки зрения именно такой подход соблюдает GRASP, а не нарушает его. И наоборот — если смешать логику и данные, то GRASP будет нарушен (как и многие другие принципы).

Но как раз для таких случаев у нас не только написано требование соблюдения GRASP, а есть подробные правила. Они расшифровывают этот и другие принципы и описывают, как их понимаем мы. В итоге документ создает единый стандарт. И добавлю, что на какую-то абсолютную истину мы не претендуем. Просто делимся опытом.)
Здесь похожая ситуация и та же причина. Просто я пример неудачный написал. Достаточно мой пример заменить на твой, и ответ будет тот же. В коде мы видим new User() и new Project(). Но мы не знаем, что это за классы, так как их неймспейсы где-то вверху файла. Это в четырех строчках всё понятно, а классы с логикой обычно на экран не помещаются.
Да, попробую кратко.

В большом проекте очень много классов с одинаковыми названиями, но с разными неймспейсами. Если разработчик в коде видит условное new User(), то он не может точно быть уверен, что за User тут имеется в виду. Может Service\Repository\User. Или Entity\User. Разработчику приходится переходить к началу файла и обратно. Это затрудняет чтение кода. Гораздо удобнее читать new Entity\User().

При этом подключение неймспейса через use вместо класса не добавляет никаких сложностей и абсолютно бесплатно. Даже наоборот — меньше мусорного кода вначале файла. То есть мы «забесплатно» можем упростить человеку понимание кода. Не вижу причин не сделать этого.

Видишь ли ты какие-то причины подключать именно класс и прятать неймспейс?
Да, ты правильно заметил, что это надо реализовывать через фильтр. Это будет и логичнее, и понятнее, и надёжнее. Описанный выше вариант с unset — это не наш путь.)
А можешь написать пример/псевдокод, о каком случае речь?
чёт какой то диссонанс у меня вызывают эти два предложения.

Я понимаю, почему это вызывает удивление, но на самом деле всё объяснимо. Когда весь код написан по строгим стандартам, то разработка задачи занимает всегда понятное и предсказуемое время. Например, написание кода для условной задачи может занять неделю, а прохождение PR пару дней. Если же код не придерживается никаким правилам, то PR может пройдёт за пару часов, вот только разработка может занять 2 недели или дольше. В итоге PR проходит дольше, но «Time-To-Market» у задач намного меньше.

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

Обычно слово «legacy» несет негативную окраску и по сути означает «дурно пахнущий код». По крайней мере в статье оно применено именно в таком смысле. Четкие правила и стандарты помогают, если не избавиться полностью, то свести к минимуму процент такого кода.
Правильная мысль. Мы активно используем Code Sniffer и у нас в нём написано довольно много проверок. Они отрабатывают на Pre-Receive Hook, не давая запушить код с нарушениями. Однако поддерживать эти проверки в актуальном состоянии довольно проблемно (кто писал правила под CS, тот поймет). Так что пока некоторые вещи дешевле внести в текстовый документ и не тратить время. Мы всё ждем, когда же появится какой-то аналог CS, но для людей. Чтобы правила можно было делать без слёз и боли. :<
Дело в том, что strace генерирует эту статистику по завершении программы, которую он пасет, которую он трассирует, и если у вас работает nginx, а вы не хотите его прерывать, то вы не получите этой статистики

Strace без проблем подцепляется к уже запущенному процессу. Это, ведь, даже в этом же материале демонстрировалось. В том числе без проблем подцепляется с флагом -c. Или автор что-то другое имел в виду?
Когда это происходило?
Со мной по-английски говорили по телефону. Возможно у них что-то поменялось.

Не забывайте, что это лоукостер с одними из самых низких цен из тех, что я видел. Дешевле, чем hetzner и ovh. В ovh мне, например, быстрее, чем за несколько дней, на тикет не отвечали. Так что всё относительно.
Добавлю к словам, сказанным o6CuFl2Q.

Cassandra — это не колоночная СУБД. Сравнивать ее с ClickHouse нельзя.

Cassandra — key-value хранилище «на стероидах». Она конкурирует с HBase, Aerospike и подобными. Вы можете очень быстро записывать туда данные. Можете очень быстро получить любую запись по ключу. Но вы не можете сделать count(id) на петабайтовом кластере за секунду.
Разумеется и у Cassandra, и у остальных конкурентов есть возможности делать аналитические запросы через MapReduce, однако из-за неколоночной структуры хранения данных такие запросы отнимают все равно слишком много времени и ресурсов.

Сравнивать ClickHouse надо с Kudu и EventQL.
1

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity