Часть 1 — Дерьмовые определения базы данных и функциональные идеалы

Это требует некоторой истории. Потерпите немного, пока я готовлю сцену.

Несколько лет назад наша компания наняла кодировщика со стороны, потому что я был единственным программистом в штате и руководил большим проектом. Руководство решило, что нам срочно нужна эта новая система. Я разработал очень специфический дизайн: API для загрузки веб-сайта (а позже и мобильного приложения), веб-сайт на чистом HTML, административный сайт на чистом HTML. API должен был быть написан на NodeJS или Go. Сайты на «чистом HTML» должны быть переносимыми и не зависеть от конкретных настроек хостинга. Я должен иметь возможность запускать их из каталога на моем компьютере или из Digital Ocean, BlueHost, Heroku и т. д. Все данные должны храниться в базе данных PostgreSQL, которую можно прикрепить через URL-адрес. Все данные зашифрованы при хранении (мы были компанией по разработке медицинского программного обеспечения). Не может быть проще. Верно? Я уточнил это, получил разрешение и с тех пор передал управление проектом другому члену команды. Я вернулся к своему мобильному приложению + API + проекту базы данных. Вместо этого мы получили систему LAMP уровня 1995 года (и качества) с MySQL, а не с PostreSQL, и без API.

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

Недавно в этой куче мусора меня попросили оценить работу по добавлению двух новых фич. Имейте в виду: «Нам отчаянно нужно переписать это» — это не та фраза, которую руководство физически способно услышать, я думаю, несмотря на то, сколько раз я говорил ее за последние пару лет. Я почти уверен, что улитка просто не обрабатывает эти звуки. Я не заходил в эту систему около трех лет, и когда я это сделал тогда, он был под мокрым одеялом презрения и гнева из-за того, как сильно мы облажались, и как я был зол на себя за то, что доверял буквально всем, кто в этом участвовал. .

Что я забыл о входе в эту систему, так это то, что домашняя страница (в / (root) ) и панель администратора (в /admin или /portal или /backoffice, потому что зачем делать в одном URL то, что вы можете сделать в 2 или 3) использовать разные логины. Так что, хотя я сбросил пароль и очень осторожно набрал /, функция забыла пароль, это не имело значения, потому что в этой системе вы можете быть пользователем ИЛИ вы можете быть администратором; никогда оба. В /admin не было функции забытого пароля, и как только я со всем этим разобрался — углубившись в базу данных и поработав с кодом, — я смог получить ответы для руководства. Кстати, все эти ответы были «нет». Мы сохраним это на потом.

Вот некоторые из плохих решений, которые я обнаружил на этом пути.

Когда роль предписывающая до бесполезности

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

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

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

Когда идентификатор базы данных не является идентификатором базы данных

Как правило, идентификатор базы данных — в какой бы форме он ни принимался для любого данного набора записей в любой данной базе данных — является уникальным указателем на определенную строку данных (или документ в no-sql) в базе данных. Вообще говоря, любой объект базы данных, который может быть однозначно идентифицирован, будет иметь для этого определенный способ — идентификатор. Этот идентификатор, опять же, обычно представляет собой числовое поле, которое просто увеличивается по мере добавления новых элементов, но также может быть строкой, GUID/UUID, набором нескольких полей (уникальный ключ) или управляемым системой способом доступа к заданная запись (автоматически сгенерированный entity_id в некоторых базах данных без SQL). Суть в том, что… это должно быть значение, которое выбирает заданное значение, оно всегда уникально в домене данных, и, хотя это не жесткое правило, обычно хорошо и ясно названо (id, user_id, Vehicle_id и т. д.).

Когда я выбирал пользовательские записи, пытаясь найти мой конкретный логин, я нашел поле role_id, и, выбирая отдельное значение для этого значения, я нашел ранее обсуждавшиеся четыре роли, соответственно (?) Идентифицированные как 1 , 2, 3 и, конечно же, 4.

Небольшое примечание: многие системы с ролями и разрешениями могут использовать систему с возрастающим значением, в которой каждый уровень разрешений, скажем, 10 может делать все то, что могут делать уровни разрешений 1–9, плюс еще что-то, чего не может делать 9, т.е. включает в себя то, что 8 не может сделать, и так далее. В этом приложении вообще нет модели разрешений, а есть только задачи, доступные ролям, и опять же… эти роли никогда не делят задачи после входа в систему.

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

Ничего подобного я не нашел.

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

if ($userInfo['role_id'] == 1) {
            $this->data['role'] = $this->language->get('ROLE_ADMIN');
        } else if ($userInfo['role_id'] == 2) {
            $this->data['role'] = $this->language->get('ROLE_NONPROFIT');
        } else if ($userInfo['role_id'] == 3) {
            $this->data['role'] = $this->language->get('ROLE_MARKETPLACE');
        }else if ($userInfo['role_id'] == 4) {
            $this->data['role'] = 'Community User';
        }

$userInfo['role_id'] разбросан по всему приложению как способ решить, какие функции отображать для пользователя, а какие нет, и эти роли не управляются каким-либо источником данных, который может обеспечить расширяемость или расширение ролей в любом сплоченный, гибкий способ. Вместо этого эти четыре роли являются концом текущей модели приложения, и на эту модель не было потрачено абсолютно никаких мыслей о будущем.

Все либо жестко запрограммировано, либо использует магические числа, либо в нескольких местах делает предположения в блоке } else { о том, что показывать пользователю. Это полностью ограничивающий, ограниченный, плохо спланированный дизайн. И с точки зрения кодеров, даже если они хотели выжать из нас все до последнего доллара (что они явно пытались), они даже усложнили себе жизнь, делая это таким образом.

PHP != Node, даже для исключительно узловой версии PHP

Как я упоминал ранее, это приложение должно было быть основано на API, к которому веб-сайт на чистом html мог бы получить доступ без проприетарных библиотек, присущих данной платформе, и к которому можно было бы получить доступ из мобильных приложений. Вместо этого эта компания использовала данные сеанса в браузере для всей полезной нагрузки аутентификации и т. Д., везде использовала встроенный PHP и, по сути, проигнорировала всю мою спецификацию дизайна. Опять же, я виню себя за окончательный результат, поскольку я был сосредоточен на чем-то другом и больше не думал об общем проекте после того, как получил одобрение.

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

Так держать, Команда. И вперед, в Expert Coding House. Молодцы, все мы. Спасибо за всю рыбу. 42 и иди нахуй.

Часть 2 — https://medium.com/@chris.hornberger/ill-never-use-a-coding-house-again-dbd1cff353d7