<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Проекты · Yet another&#8230;</title>
	<atom:link href="https://r.zezh.ru/projects/feed/" rel="self" type="application/rss+xml" />
	<link>https://r.zezh.ru</link>
	<description></description>
	<lastBuildDate>Thu, 13 Aug 2026 09:08:12 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>
	<item>
		<title>PLACS – немного о SUB и DOM?</title>
		<link>https://r.zezh.ru/projects/placs-%d0%bf%d0%be%d0%b3%d0%be%d0%b2%d0%be%d1%80%d0%b8%d0%bc-%d0%be-sub-%d0%b8-dom/</link>
		
		<dc:creator><![CDATA[rbdz]]></dc:creator>
		<pubDate>Thu, 13 Aug 2026 08:57:02 +0000</pubDate>
				<guid isPermaLink="false">https://r.zezh.ru/?post_type=project&#038;p=136</guid>

					<description><![CDATA[Мне уже не терпится рассказать тебе о Сабике, но нет времени дописать статью.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Ох! Ты и правда наткнулся на эту заглушку&#8230; Давай я тебя утешу: тут будет рассказ о прикольном PET проекте, который когда-то появится на Github</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Инвентарис как смысл жизни</title>
		<link>https://r.zezh.ru/projects/inventory-as-the-meaning-of-life/</link>
		
		<dc:creator><![CDATA[rbdz]]></dc:creator>
		<pubDate>Wed, 05 Aug 2026 18:17:59 +0000</pubDate>
				<guid isPermaLink="false">https://r.zezh.ru/?post_type=project&#038;p=66</guid>

					<description><![CDATA[Рассказываю о своем диплом проекте: Веб-ориентированная АИС «Инвентарис».
Что есть инвентаризация? Что есть бриф? Об этом и узнаем.]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Немного о важном</h2>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#171;Прежде чем написать первую строку кода, убедитесь, что законодатель уже не написал её за вас.&#187;</p>
</blockquote>



<h3 class="wp-block-heading">Инвентаризация вам не это</h3>



<p class="wp-block-paragraph">Есть одна категория проектов, которую разработчики почему-то очень любят недооценивать.</p>



<p class="wp-block-paragraph">Это не банковские системы. Не медицинские информационные комплексы. Даже не государственные порталы, где каждый второй запрос сопровождается молитвой и резервной копией. Это обычная&#8230; инвентаризация.</p>



<p class="wp-block-paragraph">Слово настолько скучное, что хочется немедленно закрыть вкладку и пойти писать очередной менеджер задач. Кажется, будто вся предметная область сводится к одной таблице:</p>



<pre class="wp-block-code"><code>ID | Название | Кабинет | Ответственный
</code></pre>



<p class="wp-block-paragraph">Пара кнопок <strong>«Добавить»</strong>, <strong>«Удалить»</strong>, <strong>«Изменить»</strong>, зелёная галочка и можно идти пить чай.</p>



<p class="wp-block-paragraph">К сожалению, законодательство с подобным оптимизмом категорически не согласно.</p>



<p class="wp-block-paragraph">Очень быстро выясняется, что инвентаризация существует не потому, что программисту захотелось написать красивый <a href="https://ru.wikipedia.org/wiki/CRUD" data-type="link" data-id="https://ru.wikipedia.org/wiki/CRUD">CRUD</a>. Она существует потому, что существует бухгалтерский учёт, материальная ответственность, жизненный цикл имущества и целый набор нормативных документов, определяющих, <strong>что именно считается правильным учётом</strong>. В предметной области системы для учёта оборудования именно это становится отправной точкой проектирования.</p>



<p class="wp-block-paragraph">Именно здесь проходит довольно неприятная граница между <strong>&#171;написал программу&#187;</strong> и <strong>&#171;разработал информационную систему&#187;</strong>.</p>



<p class="wp-block-paragraph">Я никогда не был против использования искусственного интеллекта при разработке. Если инструмент позволяет быстрее написать код, замечательно. Проблема начинается немного раньше&#8230; &#8230;В тот момент, когда разработчик открывает IDE, не открыв <a href="https://www.consultant.ru/" data-type="link" data-id="https://www.consultant.ru/">Консультант Плюс</a> (или любой другой аналог справочно-правовой системы в Вашей юрисдикции).</p>



<p class="wp-block-paragraph">Можно бесконечно генерировать контроллеры, ORM-модели и REST API. Всё это будет работать. Пока не выяснится, что половина процессов противоречит приказам министерства финансов, другая половина не учитывает реальные сценарии работы организации, а оставшаяся часть просто не отвечает на главный вопрос:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>зачем вообще существует эта система?</strong></p>
</blockquote>



<p class="wp-block-paragraph">В случае АИС <strong>«Инвентарис»</strong> задача с самого начала формулировалась достаточно чётко: централизованный учёт оборудования, контроль его состояния, фиксация перемещений, привязка объектов к помещениям и ответственным лицам, а также формирование необходимой отчётности.</p>



<p class="wp-block-paragraph">Заметьте одну интересную деталь!</p>



<p class="wp-block-paragraph">Нигде не написано:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#171;Создать красивый сайт.&#187;</p>
</blockquote>



<p class="wp-block-paragraph">Не написано:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#171;Использовать Flask.&#187;</p>
</blockquote>



<p class="wp-block-paragraph">Не написано:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#171;Написать PostgreSQL-запросы.&#187;</p>
</blockquote>



<p class="wp-block-paragraph">Потому что программирование является лишь способом реализации требований, а вовсе не их источником. Программирование не более чем навык, который может освоить практически каждый. И, если честно, в современном мире это далеко не самое ценное умение. Лучше будьте инженерами. Будьте великими разработчиками. Не становитесь людьми, которые умеют лишь &#171;буковки печатать&#187;. </p>



<p class="wp-block-paragraph">Я немного отвлёкся. Давайте вернёмся к теме грустной реальности (как будто до этого было весело)? Подводя итоги этого заголовка третьего уровня могу смело утверждать, что любой разработчик, который хотя бы однажды сталкивался с автоматизацией реальных процессов, довольно быстро обнаруживает неприятную истину.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Мы все подневольны.</p>



<p class="wp-block-paragraph">Подневольны законодательству.</p>



<p class="wp-block-paragraph">Подневольны внутренним регламентам организации.</p>



<p class="wp-block-paragraph">Подневольны техническому заданию.</p>



<p class="wp-block-paragraph">Подневольны ожиданиям заказчика.</p>
</blockquote>



<p class="wp-block-paragraph">И если хоть один из этих элементов проигнорировать, проект очень быстро окажется перед классическим выбором между двумя стульями. Причём оба обычно подписаны словами <strong>«переделать всё заново»</strong>.</p>



<h3 class="wp-block-heading">Pet Pet&#8217;ом</h3>



<p class="wp-block-paragraph">После <a href="https://r.zezh.ru/projects/commu-learn-from-mistakes/" data-type="project" data-id="17">статьи о <strong>«КОММУ»</strong></a> вполне закономерно возникает вопрос:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#171;Но ведь ты и раньше писал большие проекты. Почему здесь всё настолько серьёзно?&#187;</p>
</blockquote>



<p class="wp-block-paragraph">Ответ довольно простой:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>Потому что это уже не совсем pet-проект.</strong></p>
</blockquote>



<p class="wp-block-paragraph">Не поймите неправильно. На самом деле <strong>«КОММУ»</strong> проходила тоже примерно такой же путь проектирования и бла бла бла, но&#8230; Во-первых, в <a href="https://r.zezh.ru/projects/commu-learn-from-mistakes/" data-type="project" data-id="17">той статье</a> гораздо интереснее было рассказать про рефлексию дизайнера, а тут уже про <strong>&#171;правила хорошего разработчика&#187;</strong>. Во-вторых, мнение кардинально не менялось: личные проекты являются лучшим способом чему-либо научиться. Более того, именно благодаря им вообще появилась большая часть моего опыта. Но между системой, которую ты пишешь для себя, и системой, которая моделирует реальные процессы организации, существует огромная разница.</p>



<p class="wp-block-paragraph">В первом случае вполне можно позволить себе открыть <a href="https://plantuml.com/ru/" data-type="link" data-id="https://plantuml.com/ru/">PlantUML</a>, набросать диаграмму вариантов использования, подумать пять минут и перейти непосредственно к реализации. Бога ради! Декомпозируй в голове. Никто не запрещает. Иногда это даже правильно.</p>



<details class="wp-block-details has-small-font-size is-layout-flow wp-block-details-is-layout-flow"><summary>Интересно что такое декомпозиция? Нажми на меня!</summary>
<div class="wp-block-group has-small-font-size"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p class="wp-block-paragraph"><strong>Декомпозиция </strong>&#8212; это <strong>процесс</strong>, при котором мы берём <strong>сложную задачу</strong> или систему и <strong>делим </strong>её на множество более <strong>простых частей</strong>. Как заварить чай? На первый взгляд это одна простая задача, но её тоже можно декомпозировать. Давайте разобьём процесс на 5 шагов:</p>



<ol class="wp-block-list">
<li>Налить воду в чайник и включить его.</li>



<li>Положить чайный пакетик в пустую чашку.</li>



<li>После того как вода закипит, залить пакетик кипятком.</li>



<li>Подождать 3-5 минут, пока чай заварится.</li>



<li>Вытащить пакетик и выбросить его в мусор.</li>
</ol>



<p class="wp-block-paragraph"><strong>Оставляю вам домашнее задание</strong>: проведите декомпозицию процесса ядерного синтеза плутона. Блок A должен быть декомпозирован минимум до шестого уровня детализации. Дополнительно декомпозируйте несколько процессов уровня A, например A1.2 и A1.5. <strong>Удачи!</strong></p>
</div></div>
</details>



<hr class="wp-block-separator has-text-color has-alpha-channel-opacity has-background is-style-default" style="background-color:#af604c;color:#af604c"/>



<p class="wp-block-paragraph">Во втором случае подобный подход начинает разваливаться практически сразу.</p>



<p class="wp-block-paragraph">Допустим, заказчик произносит всего одну фразу:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><em>«Нам нужна система учёта оборудования.»</em></p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="663" src="https://r.zezh.ru/wp-content/uploads/2026/08/pokazalos-1024x663.webp" alt="" class="wp-image-68" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/pokazalos-1024x663.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/pokazalos-300x194.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/pokazalos-768x497.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/pokazalos.webp 1252w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
</blockquote>



<p class="wp-block-paragraph"><strong>Рисунок 1. А ты хотел по простому?</strong></p>



<p class="wp-block-paragraph"><strong>ЧТО ТУТ СЛОЖНОГО?</strong> Пять минут работы! Сейчас быстренько нагенерируем что-нибудь в&#8230; Эм&#8230; <strong><a href="https://giga.chat/" data-type="link" data-id="https://giga.chat/">GigaChat</a></strong>. Да ведь? А знаете, что Вы <strong>на самом деле</strong> должны были сделать? Как минимум задать следующие вопросы:</p>



<ul class="wp-block-list">
<li><strong>Что именно</strong> считается оборудованием?</li>



<li><strong>Кто имеет право</strong> его регистрировать?</li>



<li><strong>Можно ли</strong> удалять записи? (НЕТ! НИКОГДА!)</li>



<li><strong>Что делать</strong> при списании?</li>



<li><strong>Кто подтверждает</strong> перемещение между кабинетами?</li>



<li><strong>Как долго хранится</strong> история?</li>



<li><strong>Какие роли</strong> существуют в системе?</li>



<li><strong>Какие данные</strong> являются критическими?</li>



<li><strong>На каком сервере</strong> всё будет работать?</li>



<li><strong>Требуется ли интеграция</strong> с LDAP?</li>



<li><strong>Нужно ли журналирование</strong> действий?</li>



<li><strong>Нужно ли резервное копирование?</strong></li>
</ul>



<p class="wp-block-paragraph">Большая часть этих вопросов вообще никак не относится к программированию. Но именно они определяют, <strong>какой получится программа</strong>.</p>



<p class="wp-block-paragraph">Да, ещё существуют <strong>Федеральный закон «О бухгалтерском учёте» № 402-ФЗ</strong>, <strong>приказ Минфина России № 204н</strong>, <strong>приказ Минфина России № 4н</strong>&#8230; И ещё целая пачка нормативно-правовых актов, <strong>КОТОРЫЕ ТЫ ДОЛЖЕН БЫЛ ПРОЧИТАТЬ ПЕРЕД ТЕМ, КАК ВООБЩЕ ОТКРЫТЬ ОКНО ВВОДА ЗАПРОСА К ИИ!</strong></p>



<p class="wp-block-paragraph">Закрыл.</p>



<p class="wp-block-paragraph">Вкладку.</p>



<p class="wp-block-paragraph">С ИИ.</p>



<p class="wp-block-paragraph"><strong>Открывай справочно-правовую систему, действующую в твоей юрисдикции.</strong> Сначала разберись, <strong>что</strong> ты должен разработать. И только потом спрашивай у нейросети, <strong>как</strong> это реализовать.</p>



<p class="wp-block-paragraph">По этой причине разработка «Инвентариса» начиналась вовсе не с написания базы данных.</p>



<p class="wp-block-paragraph">Сначала появился <strong>бриф</strong> → Потом <strong>общая концепция</strong> → Затем <strong>техническое задание</strong>.</p>



<p class="wp-block-paragraph">И только после этого стало возможным переходить к проектированию самой системы. Именно такая последовательность документов зафиксирована в пояснительной записке к диплому и именно эти документы являются базой перед началом проектирования.</p>



<p class="wp-block-paragraph">Мне кажется, именно здесь многие начинающие разработчики совершают одну и ту же ошибку. Они воспринимают документацию как нечто, что существует исключительно ради преподавателя, менеджера проекта или какого-нибудь особенно сурового аудитора. На практике всё оказывается гораздо скучнее ведь документация существует ради самого разработчика.</p>



<p class="wp-block-paragraph">Почему? Потому что память человека удивительно плохо подходит для хранения проектов продолжительностью несколько месяцев. Давайте! Попробуйте через полгода вспомнить, почему в системе существует определённое ограничение. Почему именно такая модель данных. Почему пользователь не может удалить оборудование. Почему журналирование обязательно.</p>



<p class="wp-block-paragraph">Да потому что если нигде этого не записано, единственным носителем знаний остаётся автор проекта. А авторы, как показывает практика, обладают крайне неприятной привычкой забывать собственные архитектурные решения уже через несколько недель. Поэтому я совершенно перестал воспринимать бриф и техническое задание как очередную бумагу, необходимую для защиты диплома. Они стали своеобразной <strong>страховкой от самого себя</strong>. И, как выяснилось позже, довольно неплохой.</p>



<details class="wp-block-details has-small-font-size is-layout-flow wp-block-details-is-layout-flow"><summary>P.s. Нажми, чтобы открыть.</summary>
<p class="has-small-font-size wp-block-paragraph">Не хочу хвастаться (хотя… если честно, именно для этого и существуют личные блоги), но во время защиты дипломного проекта мне действительно пригодились документы, которые были разработаны как часть самой системы. Да, да. Я буквально распечатал выдержки из них вместе с выдержками из федеральных законов. В документации уже было расписано, каким требованиям законодательства соответствует каждая часть АИС, а также подробно описан выбранный стек разработки. </p>
</details>



<hr class="wp-block-separator has-text-color has-alpha-channel-opacity has-background is-style-default" style="background-color:#af604c;color:#af604c"/>



<h2 class="wp-block-heading">Давайте создавать</h2>



<p class="wp-block-paragraph">Во второй части мы наконец перейдём к самому проектированию и поговорим о вещи, которая почему-то вызывает у студентов почти физическую боль.</p>



<p class="wp-block-paragraph">Нет, не о ГОСТах. О диаграммах.</p>



<p class="wp-block-paragraph">Точнее, о том моменте, когда внезапно выясняется, что прямоугольники и стрелочки, безусловно, были созданы исключительно для концентрации страданий всех субъектов на планете Земля, однако разработчики почему-то продолжают пользоваться ими уже несколько десятилетий. Видимо, не только из чувства мазохизма.</p>



<h3 class="wp-block-heading">Проектируем</h3>



<p class="wp-block-paragraph">Как бы странно это ни звучало, но написать код в подобных проектах зачастую оказывается проще, чем его <strong>спроектировать</strong>.</p>



<p class="wp-block-paragraph">После появления технического задания появляется закономерное желание немедленно открыть IDE, создать первый маршрут Flask и начать героически писать SQL-запросы. Проблема лишь в том, что программа пока существует исключительно в голове разработчика, а минусы человеческой памяти были описаны приблизительно за 5 абзацев до этого. Помните? Нет? Ну так поэтому прежде чем появилась первая таблица MariaDB и первая HTML-страница, пришлось сделать то, что не любит практически каждый студент, увидевший слова <em>IDEF0</em> впервые.</p>



<p class="wp-block-paragraph"><strong>Рисовать диаграммы.</strong></p>



<p class="wp-block-paragraph">Нет, не потому что так захотел разработчик (он этого не хочет). И даже не потому что так требует очередной ГОСТ.</p>



<p class="wp-block-paragraph">А потому что одна диаграмма способна ответить на вопросы, на которые потом пришлось бы отвечать несколькими сотнями строк кода.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="868" data-id="73" src="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-1024x868.webp" alt="Функциональная модель IDEF0. Блок А0 &quot;регистрация оборудования&quot;" class="wp-image-73" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-1024x868.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-300x254.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-768x651.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0.webp 1500w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Функциональная модель IDEF0</figcaption></figure>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="787" data-id="72" src="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-1024x787.webp" alt="Диаграмма &quot;Сущность Связь&quot; описываюшая базу данных. Нотация IDEF1X" class="wp-image-72" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-1024x787.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-300x231.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-768x590.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-1536x1180.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-2048x1574.webp 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Диаграмма &#171;Сущность Связь&#187;</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="717" data-id="71" src="https://r.zezh.ru/wp-content/uploads/2026/08/usecase-1024x717.webp" alt="Диаграмма вариантов использования" class="wp-image-71" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/usecase-1024x717.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/usecase-300x210.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/usecase-768x538.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/usecase-1536x1075.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/08/usecase.webp 2000w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Use Case диаграмма</figcaption></figure>
</figure>



<p class="wp-block-paragraph"><strong>Галерея 1: IDEF0 • Диаграмма вариантов использования • IDEF1X</strong></p>



<p class="wp-block-paragraph">Разумеется, подробно рассматривать каждую из них в рамках статьи было бы несколько жестоко по отношению к читателю. Если Вам действительно интересно разобраться в каждой стрелочке, подписи и обозначении, настоятельно рекомендую обратиться к <strong><a href="https://r.zezh.ru/wp-content/uploads/2026/08/PZ_DP_2026.pdf" data-type="attachment" data-id="74">пояснительной записке</a></strong> (часть персональных данных скрыта для соблюдения № 152-ФЗ). Там все проектные материалы приведены полностью вместе с описанием и спецификациями. Здесь же ограничимся кратким обзором того, какую задачу решала каждая из них.</p>



<p class="wp-block-paragraph">Первой появилась <strong>IDEF0</strong>. Её задача максимально проста: ответить на вопрос <strong>«что вообще происходит внутри процесса регистрации оборудования?»</strong>. На контекстной диаграмме отображается весь процесс целиком, после чего он последовательно декомпозируется на более мелкие операции. В моём случае регистрация оборудования перестала быть банальной записью новой строки в таблицу и превратилась в последовательность действий: проверка полномочий пользователя, получение исходных данных, проверка их корректности, обращение к базе данных и фиксация результата в журнале событий. Помимо самих действий модель также показывает входные данные, управляющие воздействия, механизмы выполнения и конечный результат процесса. Именно благодаря этому становится очевидно, что регистрация оборудования представляет собой полноценный бизнес-процесс, а не одну SQL-команду.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="868" data-id="73" src="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-1024x868.webp" alt="" class="wp-image-73" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-1024x868.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-300x254.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-768x651.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0.webp 1500w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Функциональная модель IDEF0</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="611" data-id="78" src="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1-A4-1024x611.webp" alt="" class="wp-image-78" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1-A4-1024x611.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1-A4-300x179.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1-A4-768x458.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1-A4-1536x917.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1-A4-2048x1223.webp 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Декомпозиция А0 до 4 уровня</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="532" data-id="77" src="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1.1-A1.3-1024x532.webp" alt="" class="wp-image-77" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1.1-A1.3-1024x532.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1.1-A1.3-300x156.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1.1-A1.3-768x399.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1.1-A1.3-1536x798.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF0-A1.1-A1.3.webp 2000w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Декомпозиция А1 до 3 уровня</figcaption></figure>
</figure>



<p class="wp-block-paragraph"><strong>Галерея 2: Функциональная модель и её декомпозиции</strong></p>



<p class="wp-block-paragraph">После этого (ещё была вариантов использования, но она скучная даже для статьи) настало время <strong>IDEF1X</strong>. Пожалуй, именно эта диаграмма оказалась для меня наиболее интересной, поскольку она напрямую связывает требования предметной области с будущей структурой базы данных. Здесь уже появляются сущности, их атрибуты, первичные и внешние ключи, а также связи между таблицами. Центральным объектом модели становится оборудование, вокруг которого постепенно выстраиваются категории, помещения, поставщики, статусы, перемещения, обслуживание, пользователи и множество других сущностей.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="787" src="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-1024x787.webp" alt="Диаграмма &quot;Сущность Связь&quot; описываюшая базу данных. Нотация IDEF1X" class="wp-image-72" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-1024x787.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-300x231.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-768x590.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-1536x1180.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/08/IDEF1X-2048x1574.webp 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption"><strong>Рисунок 2. Диаграмма &#171;Сущность Связь&#187;</strong></figcaption></figure>



<p class="wp-block-paragraph">Последней была подготовлена <strong>диаграмма последовательностей</strong>. Если предыдущие модели описывали структуру системы, то здесь внимание уже сосредоточено на её поведении. Диаграмма показывает, каким образом взаимодействуют пользователь, веб-интерфейс, сервер приложения, база данных и сессионное хранилище во время выполнения основных сценариев работы. В качестве примеров были выбраны авторизация пользователя, загрузка списка оборудования и формирование отчётов. При этом отдельно рассматриваются не только успешные сценарии, но и типовые ошибки: недействительная сессия, недостаточные права доступа, ошибка авторизации и другие ситуации, которые в реальной эксплуатации возникают значительно чаще, чем хотелось бы любому разработчику.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="724" data-id="105" src="https://r.zezh.ru/wp-content/uploads/2026/08/authorization-1024x724.webp" alt="" class="wp-image-105" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/authorization-1024x724.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/authorization-300x212.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/authorization-768x543.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/authorization-1536x1086.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/08/authorization.webp 1684w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Процесс авторизации</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="845" data-id="106" src="https://r.zezh.ru/wp-content/uploads/2026/08/equipment_list-1024x845.webp" alt="" class="wp-image-106" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/equipment_list-1024x845.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/equipment_list-300x248.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/equipment_list-768x634.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/equipment_list-1536x1268.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/08/equipment_list.webp 1898w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Процесс загрузки</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="897" data-id="107" src="https://r.zezh.ru/wp-content/uploads/2026/08/report_request-1024x897.webp" alt="" class="wp-image-107" srcset="https://r.zezh.ru/wp-content/uploads/2026/08/report_request-1024x897.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/08/report_request-300x263.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/08/report_request-768x673.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/08/report_request-1536x1346.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/08/report_request.webp 1845w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Процесс отчёта</figcaption></figure>
</figure>



<p class="wp-block-paragraph"><strong>Галерея 3. Диаграммы последовательностей: авторизация • загрузка оборудования • формирование отчёта</strong></p>



<p class="wp-block-paragraph">Именно поэтому я старался не воспринимать проектирование как очередную бюрократическую процедуру. Оно не заменяет разработку и уж точно не делает её проще (если бы). Оно делает гораздо более важную вещь: позволяет убедиться, что будущая система хотя бы не начнёт противоречить самой себе ещё до того, как появится первая строка кода. По сути, диаграммы стали промежуточным этапом между техническим заданием и реализацией физической модели, связав требования предметной области с архитектурой будущего программного продукта.</p>



<p class="wp-block-paragraph">И только после этого можно было наконец открыть IDE и заняться тем, чего все обычно ожидают от разработчика. <strong>Написанием кода.</strong></p>



<h3 class="wp-block-heading">Пишем код</h3>



<p class="wp-block-paragraph">После завершения проектирования наступает тот самый этап, которого обычно все и ждут, услышав слово <em>«разработка»</em>. Flask, SQL-запросы, маршруты, шаблоны, авторизация&#8230; Словом, начинается написание кода. Однако даже здесь предметная область продолжает диктовать свои правила, а многие архитектурные решения были приняты задолго до появления первого <code>@app.route</code>.</p>



<p class="wp-block-paragraph">Одним из наиболее интересных решений стала <strong>версионность карточек оборудования</strong>. В большинстве простых CRUD-приложений изменение записи означает изменение строки в базе данных. В «Инвентарис» подобный подход оказался неприемлем. Если пользователь меняет помещение, статус или другую значимую характеристику оборудования, прежнее состояние не должно исчезнуть. Вместо этого система создаёт <strong>новую актуальную версию карточки</strong>, а предыдущая остаётся в базе данных в качестве архивной. Благодаря такому подходу сохраняется полная история изменений, а при возникновении спорной ситуации всегда можно восстановить последовательность действий пользователя. Более того, архивные карточки запрещено редактировать, что исключает возможность случайного изменения исторических данных. Ниже представлена функция, которая отвечает за создание новой версии оборудования. Не шедевр, но хоть что-то.</p>



<pre class="wp-block-code has-small-font-size"><code>&lt;... пропущен рендер при GET запросе ...>
    if request.method == "POST":
        try:
            # !Как видишь сначала мы проверяем на фальсификацию
            if not is_actual_equipment(item_id):
                flash("Редактирование архивной версии запрещено. Откройте актуальную запись.", "warning")
                return redirect(url_for("equipment.list_equipment"))

            # !Конечно же нельзя "перезаписать" на другое оборудование
            # !Либо пишем на него же, либо другой инвентарник
            data, errors = read_equipment_form()
            if data&#91;"inventory_number"] and db.fetch_all(
                "equipment_inventory_exists_for_other",
                data&#91;"inventory_number"],
                item_id,
            ):
                errors.append("Актуальное оборудование с таким инвентарным номером уже существует.")

            if errors:
                for error in errors:
                    flash(error, "warning")
                data&#91;"id"] = item_id
                return render_template("equipment_form.html", item=data, references=references, mode="edit")

            new_id = create_equipment_version(db, item_id, data)
            if not new_id:
                flash("Не удалось создать новую версию оборудования.", "danger")
                return redirect(url_for("equipment.view_equipment", item_id=item_id))

            flash("Создана новая версия карточки оборудования.", "success")
            return redirect(url_for("equipment.view_equipment", item_id=new_id))</code></pre>



<p class="wp-block-paragraph">При этом сама версионность не заменяет журнал перемещений, а лишь дополняет его. Например, при переносе оборудования в другое помещение система сначала создаёт новую версию карточки с обновлёнными сведениями, после чего фиксирует сам факт операции в отдельном журнале. В результате текущий список оборудования всегда отображает актуальное состояние, тогда как журнал позволяет определить, <strong>кто, когда и по какой причине</strong> выполнил перемещение.</p>



<p class="wp-block-paragraph">Не менее важной частью проекта стала <strong>модель разграничения доступа ESR</strong>, разработанная специально для данной системы. Вместо десятков отдельных булевых флагов было решено использовать компактную трёхзначную строку, где каждая позиция соответствует одному из функциональных контуров приложения: <strong>Equipment</strong>, <strong>System</strong> и <strong>Reports</strong>. Каждая цифра принимает значение от <strong>1</strong> до <strong>3</strong>, обозначая отсутствие доступа, чтение или запись соответственно. Благодаря этому права пользователя описываются всего тремя символами, а проверка доступа сводится к простому посимвольному сравнению строки пользователя со строкой, требуемой конкретным маршрутом. Например, роль <strong>311</strong> позволяет создавать и изменять оборудование, но не предоставляет доступа к системному администрированию и отчётам, тогда как <strong>333</strong> соответствует полному административному доступу. Давай посмотрим код?</p>



<pre class="wp-block-code has-small-font-size"><code># !Допустим нам нужно убедиться, что человек имеет доступ к чтению оборудованию
@equipment_bp.route("/&lt;int:item_id>")
@require_permissions("211")
def view_equipment(item_id: int) -> Any:
    pass

# !Вызываем декоратор require_permissions и говорим ему, что как минимум нужно
# !иметь право чтения (2) в контуре оборудования (E)

&lt;... Пропускаем скучный код и идём прямо к декоратору ...>

# !На всякий случай проверяем, что в БД не лезли
# !Нам интересна только схожесть с системой прав
def permissions_value(value: Any) -> str:
    raw = str(value or "").strip()
    if len(raw) != 3 or any(char not in "123" for char in raw):
        return "111"
    return raw

# !По очереди идём по каждому числу из необходимых и имеющихся
# !Какое-то число меньше? Тогда будет 403
def has_permissions(user_permissions: Any, required_permissions: str) -> bool:
    current = permissions_value(user_permissions)
    required = permissions_value(required_permissions)
    for current_digit, required_digit in zip(current, required):
        if int(current_digit) &lt; int(required_digit):
            return False
    return True

# !Загружаем права из сессии (не переживай, они с БД сверяются)
def current_permissions() -> str:
    return permissions_value(session.get("permissions"))

# !Обёртка, которая всё соберёт
def can(required_permissions: str) -> bool:
    return has_permissions(current_permissions(), required_permissions)

# !Сам код декоратора. Он вызывает обёртку и отказывает если прав нет
def require_permissions(required_permissions: str) -> Callable&#91;&#91;Callable&#91;..., Any]], Callable&#91;..., Any]]:
    def decorator(view_func: Callable&#91;..., Any]) -> Callable&#91;..., Any]:
        @wraps(view_func)
        @login_required
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            if not can(required_permissions):
                abort(403)
            return view_func(*args, **kwargs)

        return wrapper

    return decorator</code></pre>



<p class="wp-block-paragraph">Подобная схема позволила централизовать механизм безопасности. После успешной авторизации Flask сохраняет сведения о пользователе в сессии, а каждый защищённый маршрут дополнительно проверяется при помощи собственного декоратора. Если пользователь не авторизован, его перенаправляют на страницу входа. Если же прав недостаточно, выполнение обработчика прекращается ещё до начала основной логики приложения, а сервер возвращает страницу <strong>403 Forbidden</strong>. Такой подход исключает частичное выполнение операций и предотвращает обход ограничений даже при прямом вводе адреса страницы в браузере.</p>



<p class="wp-block-paragraph">Помимо проверки прав в приложении реализован и ряд прикладных механизмов защиты целостности данных. Система контролирует обязательность заполнения полей, существование ссылочных записей, корректность форматов дат и числовых значений, уникальность логинов пользователей, а также уникальность актуального инвентарного номера оборудования. Подобные проверки не относятся напрямую к модели ESR, однако позволяют предотвратить появление противоречивых или заведомо некорректных данных ещё до их сохранения в базе.</p>



<p class="wp-block-paragraph">В конечном итоге большая часть программного кода оказалась посвящена вовсе не отображению HTML-страниц и работе с SQL-запросами, а обеспечению корректности предметной области. Ведь написать форму создания оборудования значительно проще, чем гарантировать, что спустя несколько лет эксплуатации никто случайно не уничтожит историю изменений, не выдаст бухгалтеру права администратора или не создаст две актуальные карточки с одним и тем же инвентарным номером. Именно подобные ограничения и отличают информационную систему от очередного набора CRUD-страниц.</p>



<hr class="wp-block-separator has-text-color has-alpha-channel-opacity has-background is-style-default" style="background-color:#af604c;color:#af604c"/>



<h2 class="wp-block-heading">Вместо вывода</h2>



<p class="wp-block-paragraph">На этом, пожалуй, стоит поставить точку. Это всё таки публицистическая статья, а не полноценный анализ, не так ли? «Инвентарис» оказался не просто дипломным проектом, а возможностью снова пройти весь путь разработки информационной системы: от изучения законодательства и проектирования до реализации, тестирования и подготовки документации.</p>



<p class="wp-block-paragraph">Если Вам стало интересно узнать о проекте больше, ниже собраны материалы, которые могут оказаться полезными:</p>



<ul class="wp-block-list">
<li><strong><a href="https://github.com/Dazezh/invent" data-type="link" data-id="https://github.com/Dazezh/invent">GitHub-репозиторий</a></strong> — если интересно заглянуть в исходный код, посмотреть архитектурные решения или просто убедиться, что всё действительно работает.</li>



<li><strong><a href="https://www.dp.zezh.ru/user-manual">Руководство пользователя и администратора системы</a></strong> — если интересно посмотреть, как устроено взаимодействие с системой, какие сценарии работы поддерживаются и каким образом реализовано администрирование.</li>



<li><strong><a href="https://r.zezh.ru/wp-content/uploads/2026/08/PZ_DP_2026.pdf" data-type="attachment" data-id="74">Пояснительная записка к дипломному проекту</a></strong> — почти <strong>140 страниц</strong> проектирования, анализа, диаграмм, технических решений и документации. Да, часть страниц украшена чёрными плашками. Как иначе соблюдать сохранность чужих персональных данных? Зато всё остальное осталось на месте, и, если вдруг захочется погрузиться в детали, материала там более чем достаточно.</li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Комму – учись на ошибках</title>
		<link>https://r.zezh.ru/projects/commu-learn-from-mistakes/</link>
		
		<dc:creator><![CDATA[rbdz]]></dc:creator>
		<pubDate>Sat, 25 Jul 2026 15:08:26 +0000</pubDate>
				<guid isPermaLink="false">https://r.zezh.ru/?post_type=project&#038;p=17</guid>

					<description><![CDATA[Портал для общения внутри компании. Написано в конце второго курса и доработано в начале третьего.
 ]]></description>
										<content:encoded><![CDATA[
<h2 id="idea" class="wp-block-heading">Идея и цель проекта</h2>



<p class="wp-block-paragraph">Система коммуникации сотрудников <strong>«<a href="https://commu.zezh.ru/present" data-type="link" data-id="https://commu.zezh.ru/present" target="_blank" rel="noreferrer noopener">КОММУ</a>»</strong> по своей сути была именно учебным проектом во всех возможных проявлениях этого понятия.</p>



<p class="wp-block-paragraph">Началось всё в конце второго курса. Тогда в планах система ещё пафосно называлась <strong>СУБ</strong>, она же <strong>«Система управления бизнесом»</strong>, но, не выдержав конкуренции с великим и ужасным <strong>«<a href="https://www.bitrix24.ru/" data-type="link" data-id="https://www.bitrix24.ru/" target="_blank" rel="noreferrer noopener">Битрикс24</a>»</strong>, решила слегка умерить амбиции и переквалифицироваться в систему коммуникации сотрудников.</p>



<p class="wp-block-paragraph">Примерно тогда же был выбран первоначальный стек разработки, представленный в таблице 1. </p>



<p class="wp-block-paragraph"><strong>Таблица 1. Первоначальный стек разработки «КОММУ»</strong></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Компонент</th><th>Назначение</th></tr></thead><tbody><tr><td><strong>MariaDB</strong></td><td>Реляционная СУБД для хранения данных системы</td></tr><tr><td><strong>PyMySQL</strong></td><td>Драйвер для прямой работы Python с MariaDB/MySQL</td></tr><tr><td><strong>Flask</strong></td><td>Веб-фреймворк, на котором построена серверная часть приложения</td></tr><tr><td><strong>Pillow (PIL)</strong></td><td>Работа с изображениями: создание, обработка и преобразование графики</td></tr><tr><td><strong>qrcode</strong></td><td>Генерация QR-кодов</td></tr><tr><td><strong>secrets</strong></td><td>Генерация криптографически стойких случайных значений, токенов и прочих вещей, которые лучше не получать через <code>random()</code></td></tr><tr><td><strong>Bleach</strong></td><td>Очистка пользовательского HTML от потенциально опасного содержимого</td></tr><tr><td><strong>Markdown</strong></td><td>Преобразование Markdown-разметки в HTML</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Только недавно сдавшему экзамен по <strong>«Базам данных»</strong> мне казалось, что библиотеки объектно-реляционного отображения данных (<strong>ORM</strong>) являются <em>«излишней тратой времени»</em>.</p>



<p class="wp-block-paragraph">Да. Это была ужасная ошибка новичка.</p>



<p class="wp-block-paragraph">Именно благодаря этому гениальному архитектурному решению мои проекты до сих пор содержат SQL-запросы, записанные под именованными ключами<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2122.png" alt="™" class="wp-smiley" style="height: 1em; max-height: 1em;" />. Фактически это самодельный промежуточный слой между приложением и базой данных, который появился исключительно потому, что когда-то мне показалось хорошей идеей <strong>не использовать уже существующий промежуточный слой между приложением и базой данных</strong>.</p>



<p class="wp-block-paragraph">Но об этом преступлении против здравого смысла поговорим позже.</p>



<hr class="wp-block-separator has-text-color has-alpha-channel-opacity has-background is-style-default" style="background-color:#af604c;color:#af604c"/>



<h2 class="wp-block-heading">Дизайн</h2>



<p class="wp-block-paragraph">С чего вообще может начать веб-разработчик?</p>



<p class="wp-block-paragraph">Вопрос неоднозначный, ведь если идти по этапам, то <strong>МЫ</strong> начинаем с предпроектной стадии, затем переходим к проектированию, затем к реализации&#8230;</p>



<p class="wp-block-paragraph">Ах! Восхитительная вещь этот <strong>жизненный цикл программного продукта</strong>! Сколько схем, сколько мнений, и все почему-то уверены, что именно <strong>ИХ</strong> каскадная / спиральная / капиталистическая модель есть истина в последней инстанции&#8230;</p>



<p class="wp-block-paragraph">Вы так не думаете?</p>



<p class="wp-block-paragraph">А я думаю, что это стоит разобрать в отдельной статье, пока очередная методология не успела обзавестись манифестом, сертификацией и консультантами по её правильному внедрению.</p>



<p class="wp-block-paragraph">Сегодня же мы сразу перейдём к <strong>дизайну</strong>.</p>



<p class="wp-block-paragraph">Точнее, <strong>Я</strong> буду разбирать дизайн, а <strong>Вы</strong> постараетесь не потерять нить рефлексии ученика, который спустя время вернулся к собственному проекту и теперь вынужден выяснять, <strong>почему он вообще сделал именно так</strong>.</p>



<h3 class="wp-block-heading">Архаичный разработчик</h3>



<p class="wp-block-paragraph">Будучи ещё не до конца сформировавшимся специалистом в области дизайна, я совершил очередную непростительную для учебного проекта ошибку: <strong>не стал использовать <a href="https://getbootstrap.com/" data-type="link" data-id="https://getbootstrap.com/" target="_blank" rel="noreferrer noopener">Bootstrap</a></strong>.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><em>Непростительный шаг!</em> — скажете вы.</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><em>Ужасная трата времени для учебного проекта!</em> — добьёте вы.</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>Юношеский максимализм и боязнь неизведанного</strong>, — отвечу я.</p>
</blockquote>



<p class="wp-block-paragraph">Чем именно закончилась эта замечательная попытка изобрести фронтенд самостоятельно, можно увидеть на <strong>изображениях 1–3</strong>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="783" src="https://r.zezh.ru/wp-content/uploads/2026/07/image-1024x783.png" alt="Скриншот страницы Комму.Регламенты. Самописный CSS... Он ужасен..." class="wp-image-40" srcset="https://r.zezh.ru/wp-content/uploads/2026/07/image-1024x783.png 1024w, https://r.zezh.ru/wp-content/uploads/2026/07/image-300x229.png 300w, https://r.zezh.ru/wp-content/uploads/2026/07/image-768x587.png 768w, https://r.zezh.ru/wp-content/uploads/2026/07/image.png 1161w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>Рисунок 1. Комму.Регламенты написанные на чистом CSS</strong></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="510" src="https://r.zezh.ru/wp-content/uploads/2026/07/image-1-1024x510.png" alt="Очередное творение CSS безумца. Страница авторизации Комму." class="wp-image-41" srcset="https://r.zezh.ru/wp-content/uploads/2026/07/image-1-1024x510.png 1024w, https://r.zezh.ru/wp-content/uploads/2026/07/image-1-300x149.png 300w, https://r.zezh.ru/wp-content/uploads/2026/07/image-1-768x382.png 768w, https://r.zezh.ru/wp-content/uploads/2026/07/image-1.png 1153w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>Рисунок 2. Очередное творение CSS безумца. Страница авторизации Комму</strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="814" height="796" src="https://r.zezh.ru/wp-content/uploads/2026/07/Picture.png" alt="Страница новостей в ужасном дизайне!" class="wp-image-43" srcset="https://r.zezh.ru/wp-content/uploads/2026/07/Picture.png 814w, https://r.zezh.ru/wp-content/uploads/2026/07/Picture-300x293.png 300w, https://r.zezh.ru/wp-content/uploads/2026/07/Picture-768x751.png 768w" sizes="auto, (max-width: 814px) 100vw, 814px" /></figure>



<p class="wp-block-paragraph" id="img-3"><strong>Рисунок 3. Страница новостей в ужасном дизайне! Почему у новости есть &#171;владелец файла&#187; неизвестно. Логика была слишком сложной в то время&#8230;</strong></p>



<p class="wp-block-paragraph">К счастью, в тот момент я ещё не успел окончательно загрязнить репозиторий подобными творениями, поэтому ранний дизайн системы сохранился исключительно на скриншотах внутри проклятых файлов с названиями в духе <strong>«Курсова Работа Проектирование.финал.финал.docx»</strong>.</p>



<p class="wp-block-paragraph">Возможно, это и к лучшему. Некоторые вещи должны оставаться погребёнными в документах времён колледжа, чтобы случайный <code>git blame</code> однажды не уничтожил остатки самоуважения автора.</p>



<p class="wp-block-paragraph">Впрочем, долго этот кошмар не продлился. Ошибка была достаточно быстро осознана, после чего система <strong>полностью переехала на готовые CSS-фреймворки</strong>.</p>



<p class="wp-block-paragraph">Как выяснилось, разработчики всё-таки не зря потратили десятилетия на создание готовых компонентов интерфейса.</p>



<h3 class="wp-block-heading">Конвейерный девелопер</h3>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="404" src="https://r.zezh.ru/wp-content/uploads/2026/07/nativ_css_and_bootstrap_opti-1024x404.webp" alt="" class="wp-image-46" srcset="https://r.zezh.ru/wp-content/uploads/2026/07/nativ_css_and_bootstrap_opti-1024x404.webp 1024w, https://r.zezh.ru/wp-content/uploads/2026/07/nativ_css_and_bootstrap_opti-300x118.webp 300w, https://r.zezh.ru/wp-content/uploads/2026/07/nativ_css_and_bootstrap_opti-768x303.webp 768w, https://r.zezh.ru/wp-content/uploads/2026/07/nativ_css_and_bootstrap_opti-1536x606.webp 1536w, https://r.zezh.ru/wp-content/uploads/2026/07/nativ_css_and_bootstrap_opti.webp 1671w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>Рисунок 4. Наглядная демонстрация различий между древностью и конвейером</strong></p>



<p class="wp-block-paragraph">Пройдя все стадии принятия, было решено, что интерфейс <strong>Windows 98</strong> (иначе я не могу назвать то стилистическое решение, которое когда-то было выбрано при написании нативного CSS) всё-таки устарел примерно в 1999 году. Ну, на крайняк, в 2000-м.</p>



<p class="wp-block-paragraph">Следовательно, великую <strong>«КОММУ»</strong> необходимо было переписать с использованием современных фреймворков, коих запертые в подвалах программисты за прошедшие годы наплодили вагон и ещё примерно две сотни тележек.</p>



<p class="wp-block-paragraph">В качестве оного был выбран <strong>Bootstrap</strong>, на который как раз была ориентирована учебная программа. Решение не самое оригинальное, зато теперь не требовалось собственноручно объяснять каждому <code>&lt;button&gt;</code>, как именно ему следует выглядеть. С примером Bootstrap интерфейса можно на рисунке 5 и на <a href="https://getbootstrap.com/docs/5.3/examples/#snippets" data-type="link" data-id="https://getbootstrap.com/docs/5.3/examples/#snippets" target="_blank" rel="noreferrer noopener">официальном сайте в разделе Examples→Snippets</a>.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="917" height="1024" src="https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-14-29-58-Album-example-·-Bootstrap-v5.3-917x1024.png" alt="Наглядный пример того как выглядит типичный Bootstrap интерфейс" class="wp-image-47" srcset="https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-14-29-58-Album-example-·-Bootstrap-v5.3-917x1024.png 917w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-14-29-58-Album-example-·-Bootstrap-v5.3-269x300.png 269w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-14-29-58-Album-example-·-Bootstrap-v5.3-768x857.png 768w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-14-29-58-Album-example-·-Bootstrap-v5.3.png 1007w" sizes="auto, (max-width: 917px) 100vw, 917px" /></figure>
</blockquote>



<p class="wp-block-paragraph"><strong>Рисунок 5. Наглядный пример того как выглядит типичный Bootstrap интерфейс</strong></p>



<p class="wp-block-paragraph">Отходя от избитых шуток про жалкое конвейерное производство (или избитого меня Вами за столь рваное повествование), мне бы хотелось напомнить об одной важнейшей детали, которую нельзя забывать при создании практически любого интерфейса: <strong>психологии</strong>.</p>



<p class="wp-block-paragraph"><strong>Люди</strong> (не смотря на то, что это слово не является сказуемым я бы подчеркнул его двумя палочками, но великий WordPress не позволяет ;-;) обладают удивительно типичными паттернами поведения. Благодаря им пользователь зачастую понимает назначение элемента ещё <strong>до того, как прочитает написанный на нём текст</strong>.</p>



<p class="wp-block-paragraph">Мы ориентируемся на <strong>цвет, форму, иконку, расположение и уже знакомые визуальные образы</strong>. Интерфейс при этом как бы заключает с пользователем негласный договор: если элемент выглядит знакомо, то и вести себя он должен знакомым образом.</p>



<p class="wp-block-paragraph">Например, кнопки:</p>



<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:20px 0">
<button style="padding:10px 18px;border:0;border-radius:6px;background:#0d6efd;color:white;cursor:pointer">Продолжить</button>
<button style="padding:10px 18px;border:1px solid #6c757d;border-radius:6px;background:transparent;color:#6c757d;cursor:pointer">Отмена</button>
<button style="padding:10px 18px;border:0;border-radius:6px;background:#dc3545;color:white;cursor:pointer">Удалить</button>
</div>



<p class="wp-block-paragraph">Даже если убрать половину подписей, общий смысл остаётся довольно предсказуемым. <strong>Яркая основная кнопка</strong> привлекает внимание и обозначает главное действие. Вторичная старается не мешаться под ногами. <strong>Красная</strong> недвусмысленно намекает, что после нажатия может произойти нечто, о чём пользователь через три секунды напишет в техническую поддержку.</p>



<p class="wp-block-paragraph">Цвет, разумеется, не должен быть <strong>единственным</strong> способом передачи смысла: существуют нарушения цветового восприятия, особенности зрения и банально разные экраны. Поэтому хороший интерфейс подкрепляет цвет <strong>текстом, формой, иконкой или контекстом</strong>.</p>



<p class="wp-block-paragraph"><strong>Булочка, сосиска, булочка</strong></p>



<p class="wp-block-paragraph">Другой великолепный результат многолетней дрессировки пользователей — <strong>бургер-меню</strong>.</p>



<div style="display:inline-flex;flex-direction:column;justify-content:space-between;width:34px;height:26px;padding:8px;border:1px solid #aaa;border-radius:6px;box-sizing:content-box;margin:15px 0;cursor:pointer" aria-label="Меню">
<span style="display:block;height:3px;background:currentColor;border-radius:2px"></span>
<span style="display:block;height:3px;background:currentColor;border-radius:2px"></span>
<span style="display:block;height:3px;background:currentColor;border-radius:2px"></span>
</div>



<p class="wp-block-paragraph"><strong>Булочка. Сосиска. Булочка.</strong></p>



<p class="wp-block-paragraph">Три горизонтальные полоски сами по себе не имеют никакого врождённого значения. Однако за годы существования мобильных интерфейсов пользователи привыкли: увидел три полоски где-нибудь в верхнем углу — скорее всего, <strong>там меню</strong>.</p>



<p class="wp-block-paragraph">Причём важна не только иконка, но и <strong>ожидаемое расположение</strong>. Логотип обычно ищут сверху, навигацию рядом с ним или за бургером, поиск — в шапке, настройки — за шестерёнкой, закрытие окна — за крестиком. Пользователь не должен каждый раз исследовать интерфейс как археолог, пытающийся восстановить назначение загадочной пиктограммы исчезнувшей цивилизации.</p>



<p class="wp-block-paragraph">И здесь появляется важный принцип: <strong>предсказуемость зачастую полезнее оригинальности</strong>.</p>



<p class="wp-block-paragraph">Конечно, существуют региональные и культурные различия. В некоторых регионах планеты Земля, особенно в азиатских странах вроде <strong>Японии и Китая</strong>, исторически сформировались заметно отличающиеся подходы к плотности интерфейсов, навигации и количеству одновременно отображаемой информации. Поэтому универсального «психологически правильного интерфейса для всего человечества» не существует.</p>



<p class="wp-block-paragraph">Но внутри конкретной аудитории знакомые паттерны всё равно работают. Если пользователь тысячу раз видел крестик как «закрыть», корзину как «удалить», шестерёнку как «настройки», а бургер как «меню», заставлять его на тысяча первый раз разгадывать авторскую метафору обычно бессмысленно.</p>



<p class="wp-block-paragraph"><strong>Ух! Это было восхитительное мини-эссе, не правда ли?</strong></p>



<p class="wp-block-paragraph">А я, кажется, знаю, что вы теперь хотите сделать.</p>



<p class="wp-block-paragraph"><strong>НЕТ! НЕ ЗАКРЫВАЙТЕ САЙТ!</strong></p>



<p class="wp-block-paragraph">Я не об этом.</p>



<p class="wp-block-paragraph">Я просто хотел пошутить, что <strong>на этом сайте (великий и прекрасный r.zezh.ru) меню сделано не в виде бургера, а в виде плюсика</strong>.</p>



<div style="display:inline-flex;align-items:center;justify-content:center;width:42px;height:42px;border:none;border-radius:50%;font-size:30px;line-height:1;margin:15px 0;cursor:pointer;transition:transform .2s" aria-label="Меню" onmouseover="this.style.transform='rotate(45deg)'" onmouseout="this.style.transform='rotate(0deg)'">+</div>



<p class="wp-block-paragraph">Да.</p>



<p class="wp-block-paragraph">Действительно.</p>



<p class="wp-block-paragraph">Зачем тогда я только что описывал всю эту психологию?<strong> Потому что фреймворки убирают с разработчика обязанность тратить сотни тысяч рублей на исследования — они, зачастую, уже оптимизированы под эти паттерны.</strong></p>



<h3 class="wp-block-heading">От теории к практике</h3>



<p class="wp-block-paragraph">Итак, вооружившись Bootstrap, знаниями о психологии интерфейсов и внезапным осознанием того, что <strong>не каждый элемент обязан иметь серую рамку с закруглением</strong>, можно было приступать к переделке.</p>



<p class="wp-block-paragraph">Для начала предлагаю ещё раз взглянуть на то, с чем мы имели дело изначально. Можете снова посмотреть на <a href="#img-3" data-type="internal" data-id="#img-3">Рисунок 3</a>.</p>



<p class="wp-block-paragraph">При всех издевательствах над старым дизайном стоит признать одну вещь: <strong>его основная компоновка была вполне удачной</strong>.</p>



<p class="wp-block-paragraph">Слева располагалось основное меню системы, сверху — логотип, строка поиска и элементы управления учётной записью, а всё оставшееся пространство отдавалось непосредственно содержимому страницы. Поэтому при переходе на Bootstrap я не стал сносить всё до фундамента исключительно ради возможности торжественно назвать произошедшее <em>редизайном</em>.</p>



<p class="wp-block-paragraph">Эта раскладка сохранилась.</p>



<p class="wp-block-paragraph">Более того, впоследствии она станет практически стандартной для моих личных проектов. Очень похожую организацию интерфейса можно будет встретить в <strong>PLACS</strong> и <strong>WP</strong>, статьи о которых появятся позже. Видимо, в какой-то момент я случайно изобрёл собственное представление об удобстве и с тех пор отказываюсь от него избавляться.</p>



<p class="wp-block-paragraph">Причина довольно прозаична: <strong>для настольного интерфейса такая схема чертовски удобна</strong>. Основная навигация всегда находится слева и остаётся перед глазами, глобальные элементы вынесены наверх, а центр экрана полностью принадлежит текущей задаче. Пользователю не приходится гадать, где он находится и куда исчезло меню после очередного перехода.</p>



<p class="wp-block-paragraph">Изменять требовалось не столько <em>расположение</em>, сколько <strong>визуальную иерархию элементов внутри него</strong>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="503" src="https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-08-20-Комму-Обновление-на-сайте-1024x503.png" alt="" class="wp-image-48" srcset="https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-08-20-Комму-Обновление-на-сайте-1024x503.png 1024w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-08-20-Комму-Обновление-на-сайте-300x147.png 300w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-08-20-Комму-Обновление-на-сайте-768x377.png 768w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-08-20-Комму-Обновление-на-сайте-1536x754.png 1536w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-08-20-Комму-Обновление-на-сайте.png 1869w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>Рисунок 6. Обновлённый интерфейс просмотра новости</strong></p>



<p class="wp-block-paragraph">На рисунке 6 это особенно хорошо заметно. Старый интерфейс старался показать всё одновременно и примерно с одинаковой настойчивостью. Рамка здесь, рамка там, ещё одна рамка внутри предыдущей рамки. Поля ввода, карточки, кнопки и панели отличались друг от друга настолько незначительно, что пользователю приходилось сначала <strong>читать интерфейс</strong>, а уже потом им пользоваться.</p>



<p class="wp-block-paragraph">В обновлённой версии появляется нормальная визуальная иерархия.</p>



<p class="wp-block-paragraph">Заголовок новости крупнее обычного текста. Сама новость находится в отдельном визуальном блоке. Информация об авторе вынесена в собственную карточку. Основное действие <strong>«Вернуться на главную»</strong> выделено синим цветом, тогда как второстепенное <strong>«Вернуться назад»</strong> остаётся серым.</p>



<p class="wp-block-paragraph">То есть интерфейс начал сообщать пользователю не только <em>что здесь находится</em>, но и <strong>насколько это важно</strong>.</p>



<p class="wp-block-paragraph">Это небольшая разница на уровне CSS и огромная на уровне восприятия.</p>



<p class="wp-block-paragraph">Вместо десятка одинаково выглядящих элементов взгляд получает несколько точек, за которые можно зацепиться. Пользователь сначала видит заголовок и содержимое, затем информацию об авторе и уже после этого элементы навигации. Именно так и должна работать визуальная иерархия: <strong>не заставлять человека сознательно разбирать страницу на составляющие, а делать это за него</strong>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="503" src="https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-10-20-Комму-26ef13648afd7cc5fa6a-1024x503.png" alt="" class="wp-image-49" srcset="https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-10-20-Комму-26ef13648afd7cc5fa6a-1024x503.png 1024w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-10-20-Комму-26ef13648afd7cc5fa6a-300x147.png 300w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-10-20-Комму-26ef13648afd7cc5fa6a-768x377.png 768w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-10-20-Комму-26ef13648afd7cc5fa6a-1536x754.png 1536w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-10-20-Комму-26ef13648afd7cc5fa6a.png 1869w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>Рисунок 7. Интерфейс просмотра содержимого файла</strong></p>



<p class="wp-block-paragraph">Та же логика используется и на странице файла. Имя файла, отправитель и дата собраны в одном информационном блоке, после которого сразу расположены доступные действия.</p>



<p class="wp-block-paragraph">Причём действия снова различаются визуально: <strong>«Скачать файл»</strong> является основным и поэтому выделено синим, а переход в мессенджер и возврат на главную являются вспомогательными и не пытаются перетянуть внимание на себя.</p>



<p class="wp-block-paragraph">Отдельно помогают иконки. Стрелка загрузки, сообщение и домик позволяют распознать назначение кнопок ещё до чтения текста. Разумеется, подписи всё равно оставлены, потому что превращать корпоративную систему в экзамен <em>«угадайте значение пиктограммы»</em> никто не собирался.</p>



<p class="wp-block-paragraph">Но лучше всего преимущества новой системы компонентов видны там, где интерфейс становится действительно сложным.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="503" src="https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-11-09-Комму-Мессенджер-1024x503.png" alt="" class="wp-image-50" srcset="https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-11-09-Комму-Мессенджер-1024x503.png 1024w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-11-09-Комму-Мессенджер-300x147.png 300w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-11-09-Комму-Мессенджер-768x377.png 768w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-11-09-Комму-Мессенджер-1536x754.png 1536w, https://r.zezh.ru/wp-content/uploads/2026/07/Screenshot-2026-07-26-at-15-11-09-Комму-Мессенджер.png 1869w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><strong>Рисунок 8. Интерфейс мессенджера «КОММУ»</strong></p>



<p class="wp-block-paragraph">Мессенджер одновременно содержит <strong>список чатов, текущую переписку, участников, сообщения, вложения и поле ввода</strong>. В старой стилистике всё это довольно быстро превратилось бы в братскую могилу серых прямоугольников.</p>



<p class="wp-block-paragraph">Здесь же каждая область имеет понятную функцию и своё место.</p>



<p class="wp-block-paragraph">Список диалогов закономерно находится слева, текущая переписка занимает большую центральную область, информация о выбранном чате и его участниках вынесена вправо и может быть скрыта нажатием на информационную кнопку i. Собственные сообщения выделены синим и прижаты к правой стороне, благодаря чему переписку можно визуально разделить на <strong>«я» и «остальные»</strong>, даже практически не читая её.</p>



<p class="wp-block-paragraph">Поле ввода при этом находится внизу, то есть ровно там, где пользователь ожидает найти его после примерно любого мессенджера, созданного человечеством за последние двадцать лет.</p>



<p class="wp-block-paragraph">И вот здесь проявляется главное преимущество перехода на готовый фреймворк.</p>



<p class="wp-block-paragraph">Bootstrap не сделал интерфейс хорошим <strong>автоматически</strong>. Если достаточно постараться, отвратительный интерфейс можно написать на чём угодно, и люди ежедневно героически это доказывают. Я не шучу! Есть целая <a href="https://www.reddit.com/r/badUIbattles/" data-type="link" data-id="https://www.reddit.com/r/badUIbattles/" target="_blank" rel="noreferrer noopener">ветка Reddit</a>, посвящённая этому, и мини-игра под названием <strong><a href="https://userinyerface.com/" data-type="link" data-id="https://userinyerface.com/" target="_blank" rel="noreferrer noopener">User Inyerface</a></strong> за авторством бельгийской студии Bagaar. Кстати, советую поиграть, если не хотите заставлять пользователей страдать. <strong>А если не поиграете, есть второй путь: можете, как я, самостоятельно разработать говно из Windows 98, посмотреть на него спустя год и уже на собственном опыте понять, зачем специалисты последние лет тридцать изучали UX и его психологию. Тоже работает, но первый вариант несколько дешевле.</strong></p>



<p class="wp-block-paragraph">И так, вернёмся к фреймворку. Он дал готовый и, что гораздо важнее, <strong>последовательный визуальный язык</strong>: одинаковые отступы, состояния кнопок, карточки, формы, цвета действий и понятную иерархию компонентов. Благодаря этому страницы, выполняющие совершенно разные задачи, всё равно воспринимаются частями <strong>одной системы</strong>.</p>



<figure class="wp-block-pullquote has-medium-font-size" style="border-style:none;border-width:0px;border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-left-radius:0px;border-bottom-right-radius:0px"><blockquote><p>Старый интерфейс требовал от пользователя изучить, <strong>как устроена «КОММУ»</strong>. Новый интерфейс в гораздо большей степени позволял использовать уже имеющиеся знания о том, <strong>как вообще принято пользоваться современными приложениями</strong>.</p><cite>не автор старого интерфейса (автор старого интерфейса)</cite></blockquote></figure>



<p class="wp-block-paragraph">И именно это, пожалуй, было главным результатом редизайна. Не красивые кнопочки. Не модные скругления. И даже не избавление от эстетики Windows 98.</p>



<p class="wp-block-paragraph"><strong>Пользователю наконец перестали мешать пользоваться системой.</strong></p>



<hr class="wp-block-separator has-text-color has-alpha-channel-opacity has-background is-style-default" style="background-color:#af604c;color:#af604c"/>



<h2 class="wp-block-heading">Помимо кнопок?</h2>



<p class="wp-block-paragraph">Интересное название главы, не правда ли? Интрига, поиск скрытого смысла автора, попытка понять, что же он хотел этим сказать&#8230;</p>



<p class="wp-block-paragraph"><strong>Нет. Этого не будет.</strong></p>



<p class="wp-block-paragraph">Увы и ах, но мы переходим к той части проекта, которую мне так и не удалось отрефлексировать в полной мере. Почему? Потому что принцип <strong>«не мешай пользователю пользоваться»</strong> в моей системе координат почему-то оказался значительно выше принципа <strong>«не мешай системе быстродействовать»</strong>.</p>



<p class="wp-block-paragraph">Чем это закончилось?</p>



<p class="wp-block-paragraph">Как минимум <strong>периодическими зависаниями</strong>, неоптимальными запросами и несколько неправильно настроенным кэшированием, которое уже никогда не будет исправлено. Не потому, что исправить невозможно. И даже не потому, что я не знаю, как это исправить сейчас. Просто <strong>учебные проекты учебными проектами, а двигаться дальше всё-таки нужно</strong>.</p>



<p class="wp-block-paragraph">Запомни, читатель! В какой-то момент рефакторинг старого кода перестаёт быть обучением и превращается в археологическую реставрацию собственного говна. Пользы всё меньше, кисточка всё мельче, а впереди уже лежат проекты, в которых эти ошибки можно <strong>просто не совершать заново</strong>.</p>



<h3 class="wp-block-heading">База данных</h3>



<p class="wp-block-paragraph">Как уже стало известно из главы <strong>«<a href="#%d0%b8%d0%b4%d0%b5%d1%8f-%d0%b8-%d1%86%d0%b5%d0%bb%d1%8c-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0" data-type="internal" data-id="#%d0%b8%d0%b4%d0%b5%d1%8f-%d0%b8-%d1%86%d0%b5%d0%bb%d1%8c-%d0%bf%d1%80%d0%be%d0%b5%d0%ba%d1%82%d0%b0">Идея и цель проекта</a>»</strong>, к тому моменту мною лишь недавно был закрыт экзамен по предмету <strong>«Проектирование и разработка баз данных»</strong>. Ещё не успевшие окончательно уложиться в голове нормальные формы, различия между реляционной и иерархической моделями и прочие прекрасные плоды многолетнего развития теории баз данных породили дьявольскую структуру, чья основная задача формулировалась примерно как <strong>«хранить все данные пользователей в одном месте»</strong>.</p>



<p class="wp-block-paragraph">Это была ошибка. <strong>Ужасная ошибка.</strong></p>



<p class="wp-block-paragraph">Думаю, стоит начать с малого. <strong>Юные разработчики!</strong> Прильните к экрану и проанализируйте следующий фрагмент:</p>



<pre class="wp-block-code has-small-font-size"><code>CREATE TABLE `employees` (
  `EmployeeID` int(11) NOT NULL,
  `UserLogin` varchar(100) NOT NULL COMMENT 'Логин ЛК сотрудника',
  `UserPassword` varchar(100) NOT NULL COMMENT 'Пароль ЛК сотрудника',
  `FirstName` varchar(100) NOT NULL COMMENT 'Имя сотрудника',
  `LastName` varchar(100) NOT NULL COMMENT 'Фамилия сотрудника',
  `PositionID` int(11) NOT NULL COMMENT 'Идентификатор роли, к которой относится сотрудник',
  `ManagerID` int(11) DEFAULT NULL COMMENT 'За каким менаджером закреплен сотрудник?',
  `MobileNumber` varchar(15) NOT NULL COMMENT 'Мобильный телефон сотрудника',
  `CorporateNumber` varchar(15) NOT NULL COMMENT 'Корпоративный номер сотрудника',
  `BirthDate` date NOT NULL COMMENT 'День рождения сотрудника',
  `PhotoLastUpdated` timestamp NULL DEFAULT current_timestamp(),
  `AboutMe` text DEFAULT NULL COMMENT 'Сотрудник пишет что-то о себе',
  `StartDate` date NOT NULL COMMENT 'Дата создания личного кабинета',
  `Photo` longblob DEFAULT NULL COMMENT 'Содержимое изображения',
  `IsDismissed` tinyint(1) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;</code></pre>



<p class="wp-block-paragraph">Уже здесь прекрасно видно стремление запихнуть в одну сущность всё, что хотя бы отдалённо относится к сотруднику: данные учётной записи, персональную информацию, организационные связи и даже фотографию. Само по себе большое количество полей ещё не означает нарушение нормальных форм, однако подобная структура быстро превращается в неудобный монолит и связывает данные, жизненные циклы которых совершенно не обязаны совпадать.</p>



<p class="wp-block-paragraph">Но особенно прекрасна строка <code>Photo</code> с типом данных <strong><code>LONGBLOB</code></strong>. Да, изображение пользователя хранится прямо в основной таблице сотрудников. Решение не является запрещённым древними законами SQL и в отдельных системах вполне оправданно, но в данном проекте оно лишь раздувало таблицу и усложняло работу с данными там, где можно было хранить файл отдельно, оставив базе его идентификатор или путь.</p>



<p class="wp-block-paragraph">И это, к сожалению, не единичный приступ архитектурного вдохновения. Рассмотрим, например, устройство мессенджера:</p>



<pre class="wp-block-code has-small-font-size"><code>CREATE TABLE `chatmember` (
  `MemberID` int(11) NOT NULL COMMENT 'Уникальный код участника',
  `EmployeesID` int(11) NOT NULL COMMENT 'Идентификатор пользователя',
  `ChatID` int(11) NOT NULL COMMENT 'Идентификатор чата'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

CREATE TABLE `chats` (
  `ChatID` int(11) NOT NULL COMMENT 'Уникальный идентификатор чата',
  `ChatPreview` mediumblob DEFAULT NULL COMMENT 'Иконка чата',
  `ChatName` tinytext NOT NULL COMMENT 'Имя чата'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

CREATE TABLE `message` (
  `MessageID` int(11) NOT NULL COMMENT 'Уникалльный код сообщения',
  `ChatID` int(11) NOT NULL COMMENT 'Идентификатор чата',
  `SenderID` int(11) NOT NULL COMMENT 'Идентификатор пользователя-отправителя',
  `SendDate` timestamp NULL DEFAULT NULL COMMENT 'Дата отправки',
  `ContentType` int(11) NOT NULL COMMENT 'Тип отправляемого контента',
  `IsHidden` tinyint(1) NOT NULL DEFAULT 0,
  `ReplyToMessageID` int(11) DEFAULT NULL COMMENT 'В ответ на определённое сообщение',
  `ProcessingCompleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Хранит состояние обработки сообщения на сервере',
  `ProcessingHEXCode` varchar(16) NOT NULL COMMENT 'Уникальный код обработки, существует только во время этапа обработки сообщения'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

CREATE TABLE `messagecontenttype` (
  `TypeID` int(11) NOT NULL COMMENT 'Уникальный код типа сообщений',
  `Name` tinytext NOT NULL COMMENT 'Кодовое название типа сообщения. Нужно для правильной генерации внешнего вида'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

CREATE TABLE `messagewithfile` (
  `FileID` int(11) NOT NULL COMMENT 'Идентификатор файла',
  `File` longblob NOT NULL COMMENT 'Содержимое файла',
  `FileName` tinytext NOT NULL COMMENT 'Имя файла',
  `MessageID` int(11) NOT NULL COMMENT 'Идентификатор сообщения, к которому прикреплён файл'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;</code></pre>



<p class="wp-block-paragraph">Помимо гениальных комментариев вроде <strong>«Нужно для правильной генерации внешнего вида»</strong>, здесь можно заметить ещё одну характерную особенность архитектуры: <strong>это BLOB на BLOB&#8217;е и BLOB&#8217;ом погоняет</strong>. Иконка чата? <code>MEDIUMBLOB</code>. Прикреплённый файл? <code>LONGBLOB</code>. Фотография сотрудника из предыдущего примера? Разумеется, тоже <code>LONGBLOB</code>. Если данные существуют в виде последовательности байтов, значит, по мнению тогдашнего меня, им самое место непосредственно в MariaDB.</p>



<p class="wp-block-paragraph">Впрочем, гораздо интереснее таблица <code>message</code>. Поскольку логика мессенджера и без того казалась нам недостаточно сложной (см. <strong><a href="#img-3">рисунок 3</a></strong> на нём отчётливо видны проблемы с разумностью), в ней существуют замечательные поля <code>ProcessingCompleted</code> и <code>ProcessingHEXCode</code>. Они обслуживали отдельный механизм отслеживания создаваемого сообщения и были нужны исключительно потому, что <strong>я не знал о существовании <code>lastrowid</code></strong> и вместо получения идентификатора только что вставленной записи построил вокруг проблемы собственную систему.</p>



<p class="wp-block-paragraph">Да, проблема уже была решена драйвером базы данных. Мне оставалось воспользоваться готовым решением. Вместо этого я добавил два поля, дополнительное состояние обработки и шестнадцатеричный код.</p>



<p class="wp-block-paragraph"><strong>Слава богу, мне уже не придётся переписывать этот проект.</strong></p>



<p class="wp-block-paragraph">Если после увиденного у Вас всё ещё осталось нездоровое желание подробнее ознакомиться с базой данных «КОММУ», ниже я прикреплю PDF-документ со <strong>словарём данных и диаграммой «сущность–связь»</strong>. Там подобных решений значительно больше.</p>



<div data-wp-interactive="core/file" class="wp-block-file"><object data-wp-bind--hidden="!state.hasPdfPreview" hidden class="wp-block-file__embed" data="https://r.zezh.ru/wp-content/uploads/2026/07/Словарь-данных.pdf" type="application/pdf" style="width:100%;height:425px" aria-label="Вставка Словарь данных"></object><a id="wp-block-file--media-df2b6db4-027d-4359-b017-bc07a4d969a6" href="https://r.zezh.ru/wp-content/uploads/2026/07/Словарь-данных.pdf" target="_blank" rel="noreferrer noopener">Словарь данных</a><a href="https://r.zezh.ru/wp-content/uploads/2026/07/Словарь-данных.pdf" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-df2b6db4-027d-4359-b017-bc07a4d969a6">Скачать</a></div>



<p class="wp-block-paragraph">И да, мне <strong>не стыдно</strong>, поверьте. <strong>Стыдно должно быть не за старый плохой код, а за неспособность понять, почему он был плохим.</strong> В конце концов, смысл учебного проекта именно в том, чтобы спустя время открыть его и с некоторым ужасом обнаружить, что обучение действительно сработало.</p>



<hr class="wp-block-separator has-text-color has-alpha-channel-opacity has-background is-style-default" style="background-color:#af604c;color:#af604c"/>



<h2 class="wp-block-heading">Что это было?</h2>



<p class="wp-block-paragraph">Если подводить итог, то великая <strong>«КОММУ»</strong> была совсем не плохим учебным проектом. Именно с неё начался тяжкий и долгий путь к тому, что видите Вы сейчас.</p>



<p class="wp-block-paragraph">Именно после неё я начал понимать, <strong>что такое айдентика, как работает психология пользователя и почему бизнес-система почти всегда синяя</strong>&#8230;</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Что? Вам стало интересно, почему синяя?<br><br>Нет, не стало?</p>
</blockquote>



<p class="wp-block-paragraph">Так или иначе, позже в блоге появится отдельная статья, целиком и полностью посвящённая реалиям современной айдентики. Там и разберёмся, почему половина корпоративного мира однажды посмотрела на синий цвет и коллективно решила: <strong>«Да. Вот так выглядит доверие»</strong>.</p>



<p class="wp-block-paragraph">«КОММУ» действительно умела многое, и я горжусь этим проектом. Со всеми её <code>LONGBLOB</code>, странными архитектурными решениями, самодельными механизмами обработки сообщений и интерфейсом родом из прошлого тысячелетия.</p>



<p class="wp-block-paragraph">Для парня, который только начинал <strong>по-настоящему программировать</strong>, она стала идеальным полигоном для проверки собственных сил. Здесь можно было ошибаться, переделывать, изобретать уже изобретённое, не знать о существовании <code>lastrowid</code>, а через несколько лет открыть исходный код и наконец понять, <strong>почему некоторые вещи лучше было не изобретать вовсе</strong>.</p>



<p class="wp-block-paragraph">И всё же Ваш покорный автор справился.</p>



<p class="wp-block-paragraph">Если Вам по какой-то причине оказалось мало этих слов, ниже можно ознакомиться со <strong><a href="https://commu.zezh.ru/present" data-type="link" data-id="https://commu.zezh.ru/present">страницей презентации системы</a></strong>, скриншот которой также украшает эту статью. Там «КОММУ» сохранилась именно такой, какой я видел её тогда, ещё до того, как научился смотреть на собственные проекты глазами человека, которому потом придётся ими пользоваться.</p>



<p class="wp-block-paragraph"><strong>Спасибо за внимание.</strong> На странице блога Вы можете почитать и другие размышления на свободные темы.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
