<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Пшерги кодинг</title>
<subtitle>Блог разработчика. Потому что правильные вещи нужно записывать.</subtitle><link href="https://eduard-sukharev.github.io/full.xml" rel="self"/>
<link href="https://eduard-sukharev.github.io/" rel="alternate" />
<updated>2024-11-11T00:00:00Z</updated>
<id>https://eduard-sukharev.github.io/</id>
<author>
  <name>Эдуард Сухарев</name>
  <uri>https://eduard-sukharev.github.io/</uri>
</author><entry><title>Команда в Legacy проекте</title>
<link href="https://eduard-sukharev.github.io/komanda-v-legacy-proekte/"/>
<updated>2024-11-11T00:00:00Z</updated>
<id>https://eduard-sukharev.github.io/komanda-v-legacy-proekte/</id><content type="html">&lt;p&gt;Иногда кажется, что проект небольшой и можно полностью переписать его за пару месяцев в одиночку. А иной раз руки просто опускаются когда оглядываешь фронт работ.
Кажется, вам просто нужно определиться с командой.&lt;/p&gt;
&lt;h2 id=&quot;rabota-nad-legacy-v-proekte&quot;&gt;Работа над Legacy в проекте&lt;/h2&gt;
&lt;p&gt;Если проект над которым вы работаете содерджит много легаси (&lt;a href=&quot;https://t.me/php_legaciology/4&quot;&gt;“опасно менять, но дорого удалить”&lt;/a&gt;), то это требует определённого навыка не только работы с кодом, но и планирования его развития. Невозможно же просто работать над чем-то новым и полностью игнорировать это чудовище в углу. Или можно? В конце концов, если проект опасно менять, то можно его “законсервировать” и просто не трогать - работает и не лезь.&lt;/p&gt;
&lt;p&gt;Но такую роскошь не всегда можно себе позволить, поэтому необходимо составить регламент обращения всех членов команды с этим чудовищем, свод правил его разделения, обновления и изменения. Чаще всего такие проекты очень медленно и сложно уходят от такого тяжёлого прошлого, поэтому без зафиксированного манифеста разработчика очень легко обнаружить, что вместо уменьшения этого монстра мы получили второго, просто чуть менее лохматого.&lt;/p&gt;
&lt;h3 id=&quot;bolshie-proekty-davno-ne-pishutsya-v-odinochku&quot;&gt;Большие проекты давно не пишутся в одиночку&lt;/h3&gt;
&lt;p&gt;Пожалуй, прошли те времена когда большой крутой проект можно было написать будучи студентом за выходные и, котрыв свою фирму, подорвать рынок или создать совершенно новую нишу.&lt;/p&gt;
&lt;p&gt;Да, для минимального прототипа вам достаточно вас самих, но каждый участник хакатонов вам подтвердит — с командой можно свернуть горы.&lt;/p&gt;
&lt;p&gt;Команда это не просто N человек, бегущих в одну сторону. Слаженная команда это синергия, которой непросто добиться, но один из ключевых факторов её функционирования — общие ценности и общая цель. Тут конечно меня много кто поправит и добавит что в ключевые факторы функционирования команды также входит ещё вот это… и вот то…&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://eduard-sukharev.github.io/images/posts/spanish-inquisition.jpg&quot; alt=&quot;Испанская инквизиция&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Но это я к тому, что для синхронизации совершенно разных людей приходится прикладывать много усилий и для правильной коммуникации вашего взгляда на развитие проекта вам придётся хорошо постараться, чтобы это видение попало в голову вашим коллегам хотя бы отдалённо так же, как это представляете вы.&lt;/p&gt;
&lt;h3 id=&quot;odin-v-pole-voin&quot;&gt;Один в поле — воин?&lt;/h3&gt;
&lt;p&gt;Но бывает ведь так - вы фрилансите или может штат сократили и вот вы в команде — один. Стоит ли формировать все эти правила работы с этим богомерзким наследием предков? Зачем и для кого писать эти коментарии и документацию с наполеоновскими планами по поеданию слона по кусочкам?&lt;/p&gt;
&lt;p&gt;Определённо стоит, и это даже важнее чем если вы работаете в “настоящей” команде. Потому что вы и есть вся ваша команда, но вместо пространства которое легко предолеть с помощью созвонов и мессенджеров, вас разделяет время и коммуникация здесь идёт только в одну сторону.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Пишите код так, будто человек, который будет его поддерживать – маньяк-психопат, который знает, где вы живете.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Слышали когда-нибудь эту &lt;a href=&quot;https://www.labirint.ru/books/272529/&quot;&gt;цитату&lt;/a&gt;? Её автор, конечно, немного преувеличивает, но с очень большой вероятностью тот, кто будет читать ваш код в будущем — это вы сами, но с другим опытом, другими знаниями, даже другой мотивацией и другим настроением.&lt;/p&gt;
&lt;p&gt;Весь ваш опыт и все ваши накопленные знания подталкивают вас к какому-то осознаному выбору при решении задач и то, чем вы руководствуетесь сейчас, возможно, будет совершенно иначе восприниматься вами в будущем.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://eduard-sukharev.github.io/images/posts/dark.jpg&quot; alt=&quot;Вы и снова вы из будущего&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Чтобы не тянуть этот воз как лебедь, рак и щука в одном лице, но в разных временных линиях, наметьте план и придерживайтесь его.&lt;/p&gt;
&lt;h3 id=&quot;ne-vysechen-v-kamne&quot;&gt;Не высечен в камне&lt;/h3&gt;
&lt;p&gt;Это конечно не значит что план нельзя менять — может и вправду его осмысленность изменится со временем так сильно, что придётся его подкорректировать или переписать, но это должно потребовать от вас явных действий, возможно даже с согласованием в команде.&lt;/p&gt;
&lt;p&gt;Чем это лучше, чем просто запомнить? Тем что наш мозг очень ленив и ненадёжен как инструмент. Он умеет подделывать воспоминания. Он эффективно срезает углы лишь бы не напрягаться. Он сметёт ваш план лавиной эмоций, переживаний и воспоминаний как нечего делать. Но любой сторонний артефакт игнорировать и подделывать сильно сложнее. Даже просто открыть план в гуглодоке и изменить его, сформулировав мысль, требует сильно больших трудозатрат, чем просто “передумать” или “забыть”.&lt;/p&gt;
&lt;p&gt;План писать необходимо, плана нужно держаться, но при необходимости его можно и нужно менять.&lt;/p&gt;
</content>
</entry><entry><title>Обновление glibc и сломанные индексы PotgreSQL</title>
<link href="https://eduard-sukharev.github.io/obnovlenie-glibc-i-slomannye-indeksy-potgresql/"/>
<updated>2024-07-22T00:00:00Z</updated>
<id>https://eduard-sukharev.github.io/obnovlenie-glibc-i-slomannye-indeksy-potgresql/</id><content type="html">&lt;p&gt;Если вы используете PosgtreSQL в качестве СУБД, то не спешите обновлять ваши сервера: можете получить на ровном месте очень нетривиальные к отслеживанию ошибки.&lt;/p&gt;
&lt;h2 id=&quot;obnovlenie-glibc&quot;&gt;Обновление glibc&lt;/h2&gt;
&lt;p&gt;Один из самых важных компонент при обновлении ОС это пакет glibc, который используется почти всем софтом в системе.&lt;/p&gt;
&lt;p&gt;1 Августа 2018 года &lt;a href=&quot;https://sourceware.org/legacy-ml/libc-alpha/2018-08/msg00003.html&quot;&gt;вышла версия 2.28&lt;/a&gt;, в которой среди крупных изменений числится:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The localization data for ISO 14651 is updated to match the 2016 Edition 4 release of the standard, this matches data provided by Unicode 9.0.0.  This update introduces significant improvements to the collation of Unicode characters.  This release deviates slightly from the standard in that the collation element ordering for lowercase and uppercase LATIN script characters is adjusted to ensure that regular expressions with ranges like [a-z] and [A-Z] don’t interleave e.g. A is not matched by [a-z].  With the update many locales have been updated to take advantage of the new collation information.  The new collation information has increased the size of the compiled locale archive or binary locales.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Изменились порядок сортировки строк и особенности классификации некоторых символов, что соответственно напрямую отразилось на работе PostgreSQL с поддержкой правил сортировки строк (COLLATIONS).&lt;/p&gt;
&lt;h2 id=&quot;primery-sortirovki-v-postgresql-s-raznymi-versiyami-glibc&quot;&gt;Примеры сортировки в PostgreSQL с разными версиями (g)libc&lt;/h2&gt;
&lt;p&gt;Найти и проверить одинаковые версии PostgreSQL на дистрибутивах с разными версиями glibc мне не удалось, однако glibc это лишь реализация Standard C library. Другой реализацией является musl, используемый в дистрибутивах Alpine в Docker. Давайте проверим, отличаются ли реализации порядка сортировки между glibc и libc.&lt;/p&gt;
&lt;p&gt;Создадим composer.yaml:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;3.9&#39;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;db_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16.3&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres
      &lt;span class=&quot;token key atrule&quot;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres
  &lt;span class=&quot;token key atrule&quot;&gt;db_2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;16.3&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;alpine
    &lt;span class=&quot;token key atrule&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres
    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres
      &lt;span class=&quot;token key atrule&quot;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;и поднимем контейнеры с помощью&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; compose up &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; --force-recreate --renew-anon-volumes&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;и запустим &lt;code&gt;psql&lt;/code&gt; в одном из них (для подключения к другому просто замените &lt;code&gt;db_1&lt;/code&gt; на &lt;code&gt;db_2&lt;/code&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker compose exec -ti db_1 psql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь сделаем запросы и сравним результаты. Вариант сортировки в PostgeSQL, в дистрибутиве Debian:&lt;/p&gt;
&lt;pre class=&quot;language-psql&quot;&gt;&lt;code class=&quot;language-psql&quot;&gt;postgres=# SELECT name FROM unnest(ARRAY[
    &#39;а&#39;, &#39;д&#39;, &#39;е&#39;, &#39;ё&#39;, &#39;ж&#39;, &#39;я&#39;
]) name ORDER BY name;
 name 
 ------
 а
 д
 е
 ё
 ж
 я
(6 rows)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Вариант сортровки в PostgreSQL, в дистрибутиве Alpine:&lt;/p&gt;
&lt;pre class=&quot;language-psql&quot;&gt;&lt;code class=&quot;language-psql&quot;&gt;postgres=# SELECT name FROM unnest(ARRAY[
    &#39;а&#39;, &#39;д&#39;, &#39;е&#39;, &#39;ё&#39;, &#39;ж&#39;, &#39;я&#39;
]) name ORDER BY name COLLATE &quot;C&quot;;
 name 
 ------
 а
 д
 е
 ж
 я
 ё
(6 rows)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как видим, сортровка строк отличается в разных дистрибутивах с разными версиями стандартной библиотеки. Это только цветочки - на самом деле роль играет и сортировка строк установленная по умолчанию в системе, и сам алгоритм в версии стандартной библиотеки.
Проблема однако гораздо серъёзнее, чем просто буква “ё” съехавшая в конец списка телефонной книги. Сортировка строк влияет как на запросы типа &lt;code&gt;SELECT ... ORDER BY ...&lt;/code&gt;, так и на b-tree индексы на текстовых полях. Это значит, что существующие индексы перестают работать корректно: кроме снижения скорости запросов мы также можем получить неконсистентность данных, т.к. уникальность в таблицах реализована через &lt;code&gt;UNIQUE INDEX&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Именно с этим мы и столкнулись - обновление серверов СУБД прошло корректно и без ошибок, однако мы заметили появившиеся ошибки вида &lt;code&gt;Duplicate key violation&lt;/code&gt;. И действительно, при наличии UNIQUE INDEX данные всё же дублировались, причём далеко не все запросы показывали наличие дублирования!&lt;/p&gt;
&lt;h2 id=&quot;chto-delat&quot;&gt;Что делать?&lt;/h2&gt;
&lt;p&gt;Очевидное решение: ребилд индексов, затрагивающих текстовые поля. Сделать это нужно после того как репликация и синхронизация данных со старого сервера на новый прошла, но до того как пускать трафик на новый сервер.&lt;/p&gt;
&lt;p&gt;А если поймали ошибки с дублированием строк? Очевидно, при перестроении индекса вы получите ошибку, т.к. фактически данные у вас дублируются. Тогда придётся сначала решить эту проблему, избавившись от дубликатов данных. Если трафика много, то лучшим решением будет полностью остановить трафик на БД и сначала решить проблему с данными, а затем уже пересобирать индекс. Будьте готовы к такому исходу, когда придётся полностью откатиться к последнему бэкапу (вы же делаете бэкапы?).&lt;/p&gt;
&lt;p&gt;Другой варант решения — добавить уникальный префикс/суффикс к строковым значениям в дублирующихся записях, что позволит быстро решить проблему, пересобрать индекс и дать системе работать дальше. В этом случае вам всё равно придётся разбираться с последствиями: скорее всего в других таблицах уже появились данные, ссылающиеся на дубликаты, поэтому просто так и удалить не получится, но по крайней мере вы выиграете время на приведение системы в порядок.&lt;/p&gt;
&lt;h2 id=&quot;kak-predotvratit&quot;&gt;Как предотвратить?&lt;/h2&gt;
&lt;h3 id=&quot;otrabotat-process-obnovleniya-bd&quot;&gt;Отработать процесс обновления БД&lt;/h3&gt;
&lt;p&gt;Учитывать подобный риск и проговаривать его с командой поддержки инфраструктуры&lt;/p&gt;
&lt;h3 id=&quot;ne-obnovlyat-glibc&quot;&gt;Не обновлять glibc&lt;/h3&gt;
&lt;p&gt;Доволно радикальный подход, который может означать отказ от обновления системы вообще. Такое подходит далеко не всем, но стоит держать его как один из действенных способов избежать проблем.&lt;/p&gt;
&lt;h3 id=&quot;perestat-zaviset-ot-glibc&quot;&gt;Перестать зависеть от glibc&lt;/h3&gt;
&lt;p&gt;Переключить PostgreSQL на ICU для работы с сортировкой строк. В этом случае вы будете также зависеть от сторонней библиотеки (libicu), но её обновление не требуется при обновлении всей системы.&lt;/p&gt;
</content>
</entry><entry><title>Тип integer в колонке id как бомба замедленного действия</title>
<link href="https://eduard-sukharev.github.io/tip-integer-v-kolonke-id-kak-bomba-zamedlennogo-dejstviya/"/>
<updated>2023-12-25T00:00:00Z</updated>
<id>https://eduard-sukharev.github.io/tip-integer-v-kolonke-id-kak-bomba-zamedlennogo-dejstviya/</id><content type="html">&lt;p&gt;Если вы работаете с Legacy проектом, то загляните в структуру ваших таблиц - возможно некоторые из них используют integer в качестве id поля, и это очень плохо.&lt;/p&gt;
&lt;h2 id=&quot;v-chyom-problema&quot;&gt;В чём проблема&lt;/h2&gt;
&lt;p&gt;Тип integer имеет лимиты от -2147483648 до +2147483647 (от 1 до 2147483647 для serial) в PostgresQL. К этому лимиту довольно легко подойти, особенно если система была создана уже значительно время назад.&lt;/p&gt;
&lt;p&gt;Особенность этой проблемы:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;она не будет тригериться деплоем или другими действиями с кодом&lt;/li&gt;
&lt;li&gt;она не проявится от аномальной нагрузки&lt;/li&gt;
&lt;li&gt;может затронуть очень разный функционал&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;kak-proverit&quot;&gt;Как проверить&lt;/h2&gt;
&lt;p&gt;Если у вас небольшой проект и таблиц немного, то можно руками и глазами проверить каждую таблицу. Однако это плохо масштабируется.&lt;/p&gt;
&lt;p&gt;Можно проверить размерность всех id колонок во всех таблицах, однако в разное время даже в одном проекте могут использоваться разные конвенции именования, поэтому часть таблиц могут иметь колонки вроде &lt;code&gt;product_id&lt;/code&gt; или &lt;code&gt;rid&lt;/code&gt; и т.п.&lt;/p&gt;
&lt;p&gt;Ещё один более масштабируемый вариант - проверить текущие значения в последовательностях используемых для автоинкрементных колонок. Для этого подойдёт такой запрос:&lt;/p&gt;
&lt;pre class=&quot;language-PostgresQL&quot;&gt;&lt;code class=&quot;language-PostgresQL&quot;&gt;SELECT c.relname, nextval(c.relname::regclass) AS seq_val
FROM pg_class c
WHERE c.relkind = &#39;S&#39;
  AND  nextval(c.relname::regclass) &gt; 1000000000
ORDER BY seq_val desc;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;В результате получим список последовательностей превысивших 1 млрд., отсортированных по убывающей. Верхний лимит integer как мы помним больше 2 млрд. поэтому значение в 1 млрд. выглядит как то, с чего стоит начать в первую очередь. По названию sequence можно довольно легко определить какая таблица её использует.&lt;/p&gt;
&lt;p&gt;Не забываем про таблицы ссылающиеся на изменённые id в качестве внешних ключей для связи многие-к-одному или многие-ко-многим. Если foreign key constraint прописаны для таких таблиц, то их можно найти следующим запросом (здесь мы ищем внешние ключи ссылающиеся на &lt;code&gt;my_table_name(id)&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-PostgresQL&quot;&gt;&lt;code class=&quot;language-PostgresQL&quot;&gt;SELECT la.attrelid::regclass AS referencing_table,
       la.attname AS referencing_column
FROM pg_constraint AS c
         JOIN pg_index AS i
              ON i.indexrelid = c.conindid
         JOIN pg_attribute AS la
              ON la.attrelid = c.conrelid
                  AND la.attnum = c.conkey[1]
         JOIN pg_attribute AS ra
              ON ra.attrelid = c.confrelid
                  AND ra.attnum = c.confkey[1]
WHERE c.confrelid = &#39;my_table_name&#39;::regclass
  AND c.contype = &#39;f&#39;
  AND ra.attname = &#39;id&#39;
  AND cardinality(c.confkey) = 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Однако foreign key constraint есть не всегда (а порой его создание может быть вообще невозможным), поэтому стоит принять это во внимание - на этом автоматизация заканчивается и дальше придётся проверять руками и глазами.&lt;/p&gt;
&lt;h2 id=&quot;na-chto-zamenit&quot;&gt;На что заменить&lt;/h2&gt;
&lt;p&gt;Простым решением для текущего состояния будет перевести тип колонки из int в bigint - запаса прочности этого типа данных должно хватить.&lt;/p&gt;
&lt;p&gt;Однако не стоит считать что bigint это серебряная пуля при выборе типа id колонки в новых таблицах - всё конечно же будет зависеть от ваших сценариев использования и предназначения таблицы, так что ответ здесь как всегда - it depends.&lt;/p&gt;
</content>
</entry><entry><title>PHP Rector и как он вам [не] поможет</title>
<link href="https://eduard-sukharev.github.io/php-rector-i-kak-on-vam-ne-pomozhet/"/>
<updated>2023-10-23T00:00:00Z</updated>
<id>https://eduard-sukharev.github.io/php-rector-i-kak-on-vam-ne-pomozhet/</id><content type="html">&lt;p&gt;Все, кто планировал переход на новые версии PHP слышали о проекте Rector для автоматизации рефакторинга и адаптации новых фич. Каждый кто завершал такой переход может рассказать пачку историй о том как и когда всё пошло не по плану.&lt;/p&gt;
&lt;h2 id=&quot;chto-takoe-rector&quot;&gt;Что такое Rector&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://getrector.com/&quot;&gt;Rector&lt;/a&gt; это инструмент автоматизации рефакторинга и апгрейда кодовой базы. Он поможет с базовыми вещами вроде добавления тайп-хинтов и конвертацией PhpDoc в аттрибуты. Более того, типичные сценарии объединены в наборы правил: например набор Php52 для миграции на PHP 5.2 содержит сразу два правила: ContinueToBreakInSwitchRector и VarToPublicPropertyRector, которые заменят &lt;code&gt;continue&lt;/code&gt; на &lt;code&gt;break&lt;/code&gt; в switch-конструкциях и заменят ключевое слово &lt;code&gt;var&lt;/code&gt; на &lt;code&gt;public&lt;/code&gt; в объявлении полей класса, соответственно. Удобно!&lt;/p&gt;
&lt;h2 id=&quot;v-chyom-podvoh&quot;&gt;В чём подвох?&lt;/h2&gt;
&lt;p&gt;Давайте рассмотрим типичный пример апгрейда PHP между мажорными версиями - ведь именно это опаснее всего и именно в таких случаях мы хотим подстелить соломку. К примеру, пусть мы хотим обновить наш страшный legacy проект с PHP 5.6 на PHP 7.0. Смотрим на несовместимые изменения в &lt;a href=&quot;https://www.php.net/manual/en/migration70.incompatible.php&quot;&gt;PHP 7.0&lt;/a&gt; и видим среди прочих:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Negative bitshifts теперь бросают эксепшен, однако в Rector нет правил на то, чтобы детектить такие случаи&lt;/li&gt;
&lt;li&gt;Удалены все ext/mysql и ext/mssql функции - Rector также не подсветит вам что вы их испльзуете&lt;/li&gt;
&lt;li&gt;Функции &lt;code&gt;set_socket_blocking&lt;/code&gt; и &lt;code&gt;set_magic_quotes_runtime&lt;/code&gt; (с алиасом &lt;code&gt;magic_quotes_runtime&lt;/code&gt;) удалены - не обнаруживается Rector&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;и др.&lt;/p&gt;
&lt;h2 id=&quot;chto-lishnego-v-rector&quot;&gt;Что лишнего в Rector?&lt;/h2&gt;
&lt;p&gt;В то же самое время в наборе правил Rector для Php70 есть следующие штуки:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Использование &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; вместо тернарных операторов&lt;/li&gt;
&lt;li&gt;Замена if/else и тернарных операторов на оператор &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Замена if+isset и тернарных операторов на &lt;code&gt;??&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Замена вложенных вызовов &lt;code&gt;dirname()&lt;/code&gt; на один с указанием глубины&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Хорошие правила, полезные, но не первой необходимости. Такие правки увеличивают и без этого потенциально крупное изменение в коде, усложняя процесс ревью. В целом отлично когда вы рутинно апгрейдитесь с одной версии пакета на другую или между минорными версиями языка.
Для случаев когда вы просто хотите чтобы при апгрейде ничего не сломалось, нужен более лаконичный набор правил.&lt;/p&gt;
&lt;h2 id=&quot;kakie-est-alternativy&quot;&gt;Какие есть альтернативы?&lt;/h2&gt;
&lt;p&gt;А в общем-то альтернативы не то чтобы есть. &lt;a href=&quot;https://packagist.org/packages/phpstan/phpstan&quot;&gt;phpstan&lt;/a&gt; и &lt;a href=&quot;https://packagist.org/packages/vimeo/psalm&quot;&gt;psalm&lt;/a&gt; могут помочь привести в порядок кодовую базу в соответствии с наборами правил, однако точно так же базовые наборы обычно слишком широки и вносят слишком много лишних изменений.&lt;/p&gt;
&lt;p&gt;Важный урок, который я вынес из общения с этими инструментами: эти инструменты не сделают волшебно, бесшовно и безболезненно.&lt;/p&gt;
&lt;p&gt;Некоторые автоматизированные правки могут прикрыть вам тыл, тогда как другие внесут ошибки &lt;a href=&quot;https://www.php.net/manual/ru/language.oop5.variance.php&quot;&gt;ковариантности и контравариантности&lt;/a&gt; в наследовании. Такие ошибки интерпретатор отловит при первой же попытке запустить проект, но что если ошибки могут проявиться только в райнтайме?
Например, добавленный &lt;code&gt;declare(strict_types=1);&lt;/code&gt; сделает ваш код более строгим и там где раньше срабатывало приведение типов, теперь появится ошибка!&lt;/p&gt;
&lt;h2 id=&quot;tak-chto-delat-to&quot;&gt;Так что делать-то?&lt;/h2&gt;
&lt;p&gt;В конечном итоге мы хотим без ошибок выкатить в продакшен код на новой версии языка.&lt;/p&gt;
&lt;p&gt;Это означает что мы хотим как можно больше ошибо найти на ранних стадиях перехода - чем раньше, тем лучше, тем дешевле. Так что самое очевидное - &lt;strong&gt;обложитесь тестами&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Если тестами обкладываться сложно, долго и дорого (потому что у вас legacy) - найдите ключевой функционал и покройте тестами хотя бы его. Вероятно чтобы покрыть его тестами вам будет нужно сначала вручную &lt;strong&gt;критические части кода хорошенько отрефакторить&lt;/strong&gt; и например уложить в классы с полноценным внедрением зависимостей, вынесением низкоуровневой работы с БД или сетью в отдельные классы, внедрением работы с интерфейсами и абстракцией вместо конкретных реализаций, и в целом обмазывании себя типизацией со всех сторон.&lt;/p&gt;
&lt;p&gt;И все проблемы автоматических инструментов сводятся к тому что они могут быть лишь настолько умными, насколько это возможно. Если ваша функция работы с БД принимает аргумент &lt;code&gt;$ref&lt;/code&gt; типа &lt;code&gt;int[]|string[]|false|null|mixed&lt;/code&gt;, то поможет вам только собственный опыт в том как этот клубок распутать и что делать дальше, потому что применение автоматизации здесь совершенно невозможно.&lt;/p&gt;
&lt;h2 id=&quot;v-zaklyuchenie&quot;&gt;В заключение&lt;/h2&gt;
&lt;p&gt;Может показаться что я утверждаю что такие инструменты как Rector, psalm и phpstan не нужны, однако это совершенно не так. Я лишь подсвечиваю тот момент, которому не всегда деляют много внимания в диферамбах о том, как быстро и легко обновить весь проект с егозависимостями.&lt;/p&gt;
&lt;p&gt;Будьте готовы к тому что инструменты сделают хуже или привнесут неочевидные ошибки, однако эти же инструменты помогут найти самые проблемные участки кода, за которые уже придётся взяться вручную.&lt;/p&gt;
&lt;p&gt;Успешного вам обновления.&lt;/p&gt;
</content>
</entry><entry><title>Объявляйте классы final по-умолчанию</title>
<link href="https://eduard-sukharev.github.io/obuyavlyajte-klassy-final-po-umolchaniyu/"/>
<updated>2023-07-26T00:00:00Z</updated>
<id>https://eduard-sukharev.github.io/obuyavlyajte-klassy-final-po-umolchaniyu/</id><content type="html">&lt;p&gt;Ключевое слово &lt;code&gt;final&lt;/code&gt; появилось в PHP вместе с понятием классов, однако зачастую им пренебрегают.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR: Всегда объявляйте класс как &lt;code&gt;final&lt;/code&gt;, если все его публичные методы являются реализацией интерфейсов класса.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;princip-open-closed&quot;&gt;Принцип Open-closed&lt;/h2&gt;
&lt;p&gt;Вспомним принцип Open-Closed из аббревиатуры SOLID:&lt;/p&gt;
&lt;p&gt;Программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения&lt;/p&gt;
&lt;p&gt;Закрытость для изменения в этом контексте означает что когда ваш код предоставляет какой-то интерфейс, этот интерфейс не должен меняться. В конце концов API это контракт, это обещание совместимости, это ответственность. Это не означает что реализация не может меняться — как раз наоборот!&lt;/p&gt;
&lt;p&gt;Однако “открытость к расширению” не означает “открытость к наследованию”. Композиция, Стратегия, EventListener, обратные вызовы - всё это рабочие способы расширять поведение без использования наследования, которые чаще всего ещё и более предпочтительны. Часто наследование делает код только более сложным и менее гибким.&lt;/p&gt;
&lt;h2 id=&quot;nasledovanie-vs-interfejsy&quot;&gt;Наследование vs Интерфейсы&lt;/h2&gt;
&lt;p&gt;Если ваш класс изначально не подразумевает наследование, то стоит сразу объявить его &lt;code&gt;final&lt;/code&gt;. Но как понять что наследование не потребуется в дальнейшем?&lt;/p&gt;
&lt;p&gt;Исходя из принципа “программировать на уровне интерфейсов, а не реализаций” следует сразу принять что либо наш класс изначально пишется как конечная реализация набора интерейсов, либо представляет собой интерфейс с частичной реализацией логики, т.е. является абстрактным классом.&lt;/p&gt;
&lt;p&gt;Само определение абстрактного класса подразумевает что его будут наследовать, &lt;code&gt;final&lt;/code&gt; здесь точно неприменим.&lt;/p&gt;
&lt;p&gt;Класс, реализующий интерфейс же может иметь наследников, однако таковые либо полностью заново реализуют всё тот же интерфейс, а значит наследование здесь лишнее, либо общие элементы реализации просятся в абстрактный класс.&lt;/p&gt;
&lt;p&gt;Таким образом получается что если ваш класс реализует интерфейс, то вполне уместно сразу же объявить его &lt;code&gt;final&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;problemy-s-testirovaniem&quot;&gt;Проблемы с тестированием&lt;/h2&gt;
&lt;p&gt;Тестовые фреймворки полагаются на возможность наследования классов для создания моков и заглушек. Как с этим быть?&lt;/p&gt;
&lt;p&gt;Можно конечно вместо объявления класса как &lt;code&gt;final&lt;/code&gt; добавить ему аннотацию (или phpdoc) &lt;code&gt;@internal&lt;/code&gt; что пометит класс, как часть внутренней реализации и хотя технически его всё ещё можно будет наследовать, мы явно объявим о своей интенции и сделаем вид что всё хорошо.&lt;/p&gt;
&lt;p&gt;Но есть способ лучше - покрыть все публичные методы такого класса соответствующим набором интерфейсов. В таком случае наш код будет работать с абстракциями, в то время как тесты смогут сгенерировать на основе интерфейсов необходимые заглушки.&lt;/p&gt;
&lt;h2 id=&quot;pochemu-final-eto-horosho&quot;&gt;Почему &lt;code&gt;final&lt;/code&gt; это хорошо&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Предотвращает формирования адских цепочек наследования вида &lt;code&gt;MailDeliveryBot&lt;/code&gt; → &lt;code&gt;Bot&lt;/code&gt; → &lt;code&gt;Admin&lt;/code&gt; → &lt;code&gt;User&lt;/code&gt; → &lt;code&gt;Core&lt;/code&gt; → &lt;code&gt;Db&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Подталкивает к использованию композиции. Скорее всего вам не нужно наследование при работе с этим классом, если изначально он не планировался как абстрактный класс.&lt;/li&gt;
&lt;li&gt;Заставляет продумывать API библиотек и решений. Наследование нарушает инкапсуляцию. Если класс явно не создавался под наследование, то можно получить проблемы с совместимостью. Трижды думайте перед тем как использовать &lt;code&gt;extends&lt;/code&gt; и используйте &lt;code&gt;final&lt;/code&gt; чтобы не заставлять других ломать голову.&lt;/li&gt;
&lt;li&gt;Подталкивает к сужению публичного API классов&lt;/li&gt;
&lt;li&gt;При необходимости &lt;code&gt;final&lt;/code&gt; можно убрать, но придётся привести веские доводы и принять это решение явно&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;v-kakih-sluchayah-primenyat-final&quot;&gt;В каких случаях применять &lt;code&gt;final&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Классы объявленные как &lt;code&gt;final&lt;/code&gt; эффективно работают только в при соблюдении следующих условий:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Класс, объявленный &lt;code&gt;final&lt;/code&gt; реализует некоторый набор интерфейсов&lt;/li&gt;
&lt;li&gt;Все публичные методы класса покрыты этими интерфейсами&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Если хотя бы один из пунктов отсутствует, то это явный признак того, что ваш код не в полной мере полагается на абстракции, а значит рано или поздно вы можете прийти к решению через наследование. В таком случае лучше сделать шаг назад и, взглянув на холст вашего проекта целиком, придумать как решить проблему с помощью введения соответствующих абстракций.&lt;/p&gt;
&lt;h2 id=&quot;bonus&quot;&gt;Бонус&lt;/h2&gt;
&lt;p&gt;Шаблон для создания PHP классов в PhpStorm:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strict_types&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;#if (${NAMESPACE})&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;NAMESPACE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;#end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
</entry><entry><title>Избегайте отрицательных условий</title>
<link href="https://eduard-sukharev.github.io/izbegajte-otricatelnyh-uslovij/"/>
<updated>2023-07-19T00:00:00Z</updated>
<id>https://eduard-sukharev.github.io/izbegajte-otricatelnyh-uslovij/</id><content type="html">&lt;p&gt;Разберём на примерах известное правило избегания отрицаний в условиях. Какие бывают отрицания и чем они опасны.&lt;/p&gt;
&lt;p&gt;Начнём с первоисточника&lt;/p&gt;
&lt;h2 id=&quot;kak-u-dyadi-boba&quot;&gt;Как у Дяди Боба&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;G29: Избегайте отрицательных условий
Отрицательные условия немного сложнее для понимания, чем положительные. Таким образом, по возможности старайтесь формулировать положительные условия. Например запись&lt;/p&gt;
&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldCompact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;предпочтительнее записи&lt;/p&gt;
&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldNotCompact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;Заметим, что в такой формулировке и с таким примером не совсем ясно чего стоит избегать - логических операторов отрицания или отрицаний при именовании переменных и методов. Однако, кажется, можно сказать что стоит избегать двойного отрицания, как очевидно сложного для восприятия условия.&lt;/p&gt;
&lt;p&gt;Что ж, давайте разбираться.&lt;/p&gt;
&lt;h2 id=&quot;operatory-otricaniya&quot;&gt;Операторы отрицания&lt;/h2&gt;
&lt;p&gt;Рассмотрим синтетический пример метода проверки авторизации пользователя и соответствующий вариант его использования:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnonymousAuthenticator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_authenticated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PublicController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;index_action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    authenticator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; AnonymousAuthenticator&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; authenticator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_authenticated&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;authentication required&quot;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# do some business logic&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Successfuly authenticated&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Классический ход - если пользователь не авторизован, то сразу же вернём ответ. Если предположить что Роберт Мартин имел ввиду избегать операторы отрицания, то какие у нас есть варианты?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Переписать условие так, чтобы не было оператора отрицания. В таком случае основная бизнес логика ляжет внутрь &lt;code&gt;if is_authenticated&lt;/code&gt;, а негативный кейс уедет в самый конец, возможно, на пару экранов, где уже будет неясно какое условие там проверялось. Кроме того, весь код получит лишний уровень вложенности, что как известно негативно влияет на его восприятие.&lt;/li&gt;
&lt;li&gt;Добавить метод &lt;code&gt;is_not_authenticated&lt;/code&gt; в класс &lt;code&gt;AnonymousAuthenticator&lt;/code&gt;. В таком случае мы либо получим дублирование логики с методом &lt;code&gt;is_authenticated&lt;/code&gt;, которое приведёт к ошибкам в рассинхронизации двух вариантов реализации, либо отрицательное условие с вызовом &lt;code&gt;is_authenticated&lt;/code&gt; переместится внутрь этого метода. Да ещё и отрицание теперь будет в названии метода.&lt;/li&gt;
&lt;li&gt;Добавить метод &lt;code&gt;is_forbidden&lt;/code&gt; в класс &lt;code&gt;AnonymousAuthenticator&lt;/code&gt;. Проблемы те же что для предыдущего варианта, но имя уже как будто не содержит отрицания.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Все три варианта выглядят сомнительно и имеют свои недостатки, однако последний содержит нюанс, который я рекомендую избегать.&lt;/p&gt;
&lt;h2 id=&quot;otricaniya-v-yazyke&quot;&gt;Отрицания в языке&lt;/h2&gt;
&lt;p&gt;Если бы в естественных языках отрицание выражалось только частицами “не” и “нет”, то это были бы очень скудные по выразительности языки.&lt;/p&gt;
&lt;p&gt;Рассмотрим каким образом отрицания могут реализовываться в языках программаривания:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;not&lt;/code&gt; - отрицание с помощью частиц &lt;code&gt;no&lt;/code&gt; и &lt;code&gt;not&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;disable&lt;/code&gt;, &lt;code&gt;inactive&lt;/code&gt; - отрицание с помощью префиксов &lt;code&gt;in-&lt;/code&gt;, &lt;code&gt;un-&lt;/code&gt;, &lt;code&gt;dis-&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;powerOff&lt;/code&gt; - отрицание с помощью фразовых глаголов (комбинации глагола и предлога, образующие цельную смысловую единицу)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;forbid&lt;/code&gt;, &lt;code&gt;reject&lt;/code&gt; - слова с очевидным или неочевидным отрицанием в их смысловой нагрузке, но формально не попадающие под предыдущие категории&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Как ни странно, самый безобидный из этих вариантов - именно первый. Рассмотрим пример:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;EditorAccessCheck&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ENABLED&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;enabled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isNotAllowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;bool&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEditor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;NewsController&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;editAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Request&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;EditorAccessCheck&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$accessCheck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name return-type&quot;&gt;Response&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$accessCheck&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNotAllowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AccessDeniedResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Welcome to editor&#39;s club!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Использованием метода читается довольно просто и понятно, несмотря на отриание в названии. Заметим что условие, которое проверяется внутри самого метода также содержит отрицание через оператор &lt;code&gt;!&lt;/code&gt;, однако и оно не мешает воспринимать код. Проблемы начнутся когда кто-нибудь решит воспользоваться &lt;code&gt;EditorAccessCheck&lt;/code&gt; в ином контексте:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;EditorActionSubscriber&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EventSubscriber&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;EditorAccessCheck&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$accessCheck&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;supports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;bool&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;accessCheck&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNotAllowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;onEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;UserActionEvent&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// React to $event&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Здесь появляется проблема: &lt;strong&gt;двойное отрицание&lt;/strong&gt;. Как избавиться от двойного отрицания в условиях? Изменить условие так, чтобы можно было убрать одно из них. Вот только убрать таким образом можно лишь оператор отрицания, потому что для изменения названия метода придётся пройтись по всей кодовой базе и не просто изменить название вызываемого метода, но и изменить всю логику этих вызовов, чтобы сохранить работоспособность приложения.&lt;/p&gt;
&lt;h2 id=&quot;neyavnoe-dvojnoe-otricanie&quot;&gt;Неявное двойное отрицание&lt;/h2&gt;
&lt;p&gt;Все довольно просто когда мы смотрим на переменные с частицами &lt;code&gt;No&lt;/code&gt; и &lt;code&gt;Not&lt;/code&gt;, однако то же самое касается и вариантов с неявным отрицанием:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;call_when_enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;или&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; inactive&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  call_when_active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Оба варианта содержат двойное отрицание, которое сложно воспринимать, даже несмотря на то, что ни одна из переменных не имеет явного отрицания с частицами &lt;code&gt;not&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;vyvod&quot;&gt;Вывод&lt;/h2&gt;
&lt;p&gt;Сам по себе оператор отрицания не несёт в себе ничего плохого и не может рассматриваться как запах кода. Проблемы с пониманием кода возникают при двойном отрицании, которое можно легко исправить если отрицание не заложено — явно или неявно — в имена переменных и методов.&lt;/p&gt;
&lt;h2 id=&quot;some-shit&quot;&gt;Some shit&lt;/h2&gt;
&lt;p&gt;Рассмотрим примеры из реальных проектов. Например, отрицание проверки отрицательного флага&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$widget&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFlagEnabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;ProductFlag&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DISABLED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Чтобы каждый раз не пытаться понять что имелось ввиду, можно инкапсулировать эту логику в метод с понятным названием:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isActive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;bool&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFlagEnabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;ProductFlag&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DISABLED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Хотя кажется правильней было бы ориентироваться на константу &lt;code&gt;ProductFlag::ENABLED&lt;/code&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Ещё один пример в коде, который пока нельзя назвать проблемой, но уже явно попахивает:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;WidgetFlag&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DISABLED&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x0001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DISABLED&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Выключено&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Что мы видим:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Константа с неявным отрицательным смыслом;&lt;/li&gt;
&lt;li&gt;Из значения &lt;code&gt;0x0001&lt;/code&gt; можно сделать предположение, что есть, вероятно среди возможных значений хранящихся в БД или приходящих по API, ещё неявная положительная константа &lt;code&gt;ACTIVE = 0x0000;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;Иногда плохие примеры получаются из-за проблем самого языка. Например в некоторых языках различные типы могут неявно приводиться к булеву типу, причем с различными побочными эффектами. И бывает так что для борьбы с этим выбирают не самые удачные решения:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isAdmin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;admin&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;admin &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;Прилагательное вместо глагола inactive -&amp;gt; deactivate / setInactive:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;inactive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;isActive&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;vmesto-zaklyucheniya&quot;&gt;Вместо заключения&lt;/h2&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;disabled&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Мы вероятно уже привыкли и это уже не исправить, но это отвратительно: двойное отрицание которое всё же отрицание&lt;/p&gt;
</content>
</entry></feed>
