Настроил презентацию
This commit is contained in:
		
							parent
							
								
									66308fe472
								
							
						
					
					
						commit
						3e4f36f972
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,7 @@ | |||||||
| # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||||||
| 
 | 
 | ||||||
|  | .idea | ||||||
|  | 
 | ||||||
| # dependencies | # dependencies | ||||||
| /node_modules | /node_modules | ||||||
| /.pnp | /.pnp | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | build: | ||||||
|  | 	yarn export | ||||||
|  | deploy: | ||||||
|  | 	scp -r ./out/* rinsvent@188.225.77.88:/home/rinsvent/dev/gateway_data/static/tbd | ||||||
| @ -2,6 +2,17 @@ | |||||||
| const nextConfig = { | const nextConfig = { | ||||||
|   reactStrictMode: true, |   reactStrictMode: true, | ||||||
|   swcMinify: true, |   swcMinify: true, | ||||||
|  |   images: { | ||||||
|  |     loader: "imgix", | ||||||
|  |     path: "https://tbd.rinsvent.ru/", | ||||||
|  |   }, | ||||||
|  |   webpack(config) { | ||||||
|  |     config.module.rules.push({ | ||||||
|  |       test: /\.svg$/, | ||||||
|  |       use: ["@svgr/webpack"] | ||||||
|  |     }); | ||||||
|  |     return config; | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| module.exports = nextConfig | module.exports = nextConfig | ||||||
|  | |||||||
| @ -6,11 +6,17 @@ | |||||||
|     "dev": "next dev", |     "dev": "next dev", | ||||||
|     "build": "next build", |     "build": "next build", | ||||||
|     "start": "next start", |     "start": "next start", | ||||||
|     "lint": "next lint" |     "lint": "next lint", | ||||||
|  |     "export": "next build && next export" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "@emotion/react": "^11.10.4", | ||||||
|  |     "@emotion/styled": "^11.10.4", | ||||||
|  |     "@fontsource/roboto": "^4.5.8", | ||||||
|  |     "@mui/material": "^5.10.11", | ||||||
|     "next": "12.3.1", |     "next": "12.3.1", | ||||||
|     "react": "18.2.0", |     "react": "18.2.0", | ||||||
|  |     "react-code-blocks": "^0.0.9-0", | ||||||
|     "react-dom": "18.2.0" |     "react-dom": "18.2.0" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  | |||||||
							
								
								
									
										455
									
								
								pages/index.tsx
									
									
									
									
									
								
							
							
						
						
									
										455
									
								
								pages/index.tsx
									
									
									
									
									
								
							| @ -2,6 +2,12 @@ import type { NextPage } from 'next' | |||||||
| import Head from 'next/head' | import Head from 'next/head' | ||||||
| import Image from 'next/image' | import Image from 'next/image' | ||||||
| import styles from '../styles/Home.module.css' | import styles from '../styles/Home.module.css' | ||||||
|  | import '@fontsource/roboto/300.css'; | ||||||
|  | import '@fontsource/roboto/400.css'; | ||||||
|  | import '@fontsource/roboto/500.css'; | ||||||
|  | import '@fontsource/roboto/700.css'; | ||||||
|  | import {CopyBlock, github} from "react-code-blocks"; | ||||||
|  | import {Table, TableBody, TableRow, TableCell, TableHead} from '@mui/material'; | ||||||
| 
 | 
 | ||||||
| const Home: NextPage = () => { | const Home: NextPage = () => { | ||||||
|     return ( |     return ( | ||||||
| @ -13,57 +19,414 @@ const Home: NextPage = () => { | |||||||
|             </Head> |             </Head> | ||||||
| 
 | 
 | ||||||
|             <main className={styles.main}> |             <main className={styles.main}> | ||||||
|         <h1 className={styles.title}> |                 <h1>Trunk-Based Development</h1> | ||||||
|           Welcome to <a href="https://nextjs.org">Next.js!</a> |                 <section> | ||||||
|         </h1> |                     Модель ветвления в системе контроля версий, в которой разработчики работают над кодом в единственной | ||||||
|  |                     ветке под названием ‘trunk’. Эта модель позволяет не создавать другие долгоживущие ветки и описывает | ||||||
|  |                     технику как именно это делать. Разработчики избегают merge конфликтов при слиянии кода, не ломают | ||||||
|  |                     сборку, и живут долго и счастливо | ||||||
|  |                 </section> | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Базовые принципы</h2> | ||||||
|  |                     <ul> | ||||||
|  |                         <li>Любые коммиты в trunk не должны ломать сборку</li> | ||||||
|  |                         <li>Любые коммиты в trunk должны быть маленькими настолько, чтобы review нового кода не занимало | ||||||
|  |                             более 10 минут | ||||||
|  |                         </li> | ||||||
|  |                         <li>Релиз выпускается только на основе trunk</li> | ||||||
|  |                     </ul> | ||||||
|  |                 </section> | ||||||
| 
 | 
 | ||||||
|         <p className={styles.description}> |                 <section> | ||||||
|           Get started by editing{' '} |                     <h2>GIT flow VS TBD</h2> | ||||||
|           <code className={styles.code}>pages/index.tsx</code> |                     <h3>Конфликты</h3> | ||||||
|         </p> |                     <Table> | ||||||
| 
 |                         <TableBody> | ||||||
|         <div className={styles.grid}> |                             <TableRow> | ||||||
|           <a href="https://nextjs.org/docs" className={styles.card}> |                                 <TableCell></TableCell> | ||||||
|             <h2>Documentation →</h2> |                                 <TableCell>GIT</TableCell> | ||||||
|             <p>Find in-depth information about Next.js features and API.</p> |                                 <TableCell>TBD</TableCell> | ||||||
|           </a> |                             </TableRow> | ||||||
| 
 |                             <TableRow> | ||||||
|           <a href="https://nextjs.org/learn" className={styles.card}> |                                 <TableCell>Auto merge</TableCell> | ||||||
|             <h2>Learn →</h2> |                                 <TableCell>Yes</TableCell> | ||||||
|             <p>Learn about Next.js in an interactive course with quizzes!</p> |                                 <TableCell>Yes</TableCell> | ||||||
|           </a> |                             </TableRow> | ||||||
| 
 |                             <TableRow> | ||||||
|           <a |                                 <TableCell>Manual merge</TableCell> | ||||||
|             href="https://github.com/vercel/next.js/tree/canary/examples" |                                 <TableCell>Yes</TableCell> | ||||||
|             className={styles.card} |                                 <TableCell>Yes</TableCell> | ||||||
|           > |                             </TableRow> | ||||||
|             <h2>Examples →</h2> |                             <TableRow> | ||||||
|             <p>Discover and deploy boilerplate example Next.js projects.</p> |                                 <TableCell>Manual merge (интеграция задачи в измененный код)</TableCell> | ||||||
|           </a> |                                 <TableCell>Yes</TableCell> | ||||||
| 
 |                                 <TableCell>No</TableCell> | ||||||
|           <a |                             </TableRow> | ||||||
|             href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" |                         </TableBody> | ||||||
|             className={styles.card} |                     </Table> | ||||||
|           > |                 </section> | ||||||
|             <h2>Deploy →</h2> |                 <section> | ||||||
|  |                     <h2>Долгоживущие ветки</h2> | ||||||
|  |                     Проблемы: | ||||||
|  |                     <ul> | ||||||
|  |                         <li>Накапливаются конфликты</li> | ||||||
|  |                         <li>Код из ветки недоступен для выполнения других задач</li> | ||||||
|  |                         <li>Параллельная разработка одного и того же механизма</li> | ||||||
|  |                         <li>Рассинхронизация логики кода в разных ветках</li> | ||||||
|  |                         <li>Ломается история GIT при конфликтах</li> | ||||||
|  |                         <li>Блокируется деплой готовой задачи, если вначале нужно задеплоить другую</li> | ||||||
|  |                         <li>Блокировка выполнения второстепенной задачи, если нужен код из другой задачи</li> | ||||||
|  |                     </ul> | ||||||
|                     <p> |                     <p> | ||||||
|               Instantly deploy your Next.js site to a public URL with Vercel. |                         При GIT flow долгоживущие ветки - это норма. У нас в проекте были ветки возрастом более года. | ||||||
|  |                         Актуализация таких веток занимала несколько дней. А ведь эти ресурсы можно было  использовать на разработку новых фич! | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                         При TBD если разработка идет непосредственно в master/trunk таких проблем нет - код актуализируется после первого pull запроса. | ||||||
|  |                         Разница с local state будет минимальна. В большинстве случаев получим fast forward. | ||||||
|  |                         При работе в feature ветке могут возникать проблемы описанные выше. Чтобы минимизировать проблемы, TBD | ||||||
|  |                         рекомендует ограничение жизнь ветки в 2 дня. | ||||||
|  |                         Это условный интервал, за который не должно накопиться много конфликтов. | ||||||
|  |                         Дополнительным положительным фактором является то, что основная часть команды продолжает писать в master, и этот код можно подливать в feature ветку | ||||||
|  |                         и тем самым уменьшать количество конфликтов, которые могут появиться в день X. Когда ветка вольется в master. | ||||||
|  |                         Важно соблюдать правило 2 дней! | ||||||
|  |                     </p> | ||||||
|  |                 </section> | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Разный state БД</h2> | ||||||
|  |                     Это порождает ошибки разработки и деплоя. | ||||||
|  |                     <ul> | ||||||
|  |                         <li>Ошибка генерации миграции. Например, при добавлении новой колонки мы указываем после какой колонки надо добавить новое поле. Но этой колонки может не оказаться на момент деплоя.</li> | ||||||
|  |                         <li>Мы не можем просто так взять и добавить колонку с constraint not null так как в других ветках код не будет работать. Это порождает тех. долг, мусорный код и ошибки, которые могут быть спровоцированы не законченным кодом.</li> | ||||||
|  |                         <li>Ошибки doctrine. Если добавить новый тип данных doctrine, но код не распространить по всем веткам, то там где его нет, будут ошибки во время калькуляции метаданных.</li> | ||||||
|  |                     </ul> | ||||||
|  |                     <p>При GIT flow различное состояние бд - это норма</p> | ||||||
|  |                     <p> | ||||||
|  |                         При следовании TBD эти проблемы исключаются. Изменение структуры бд всегда происходит в мастер. | ||||||
|  |                         Разработчикам остается актуализировать ветку и проблема решена. | ||||||
|                     </p> |                     </p> | ||||||
|           </a> |  | ||||||
|         </div> |  | ||||||
|       </main> |  | ||||||
| 
 | 
 | ||||||
|  |                 </section> | ||||||
|  | 
 | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Разработка</h2> | ||||||
|  |                     <h3>GIT flow</h3> | ||||||
|  |                     При GIT flow нужно работать над задачей в отдельной ветке | ||||||
|  |                     Задач много, следовательно веток много. | ||||||
|  |                     Все ветки различаются. Различается набор сервисов. Различается структура бд. | ||||||
|  |                     Для начала разработки нужно переключиться на ветку | ||||||
|  |                     Обычно приходится почистить кеш. Применить миграции. Для тестов переинициализировать бд. | ||||||
|  |                     Без регламента на мелкие commit`ы, провоцируются ситуации когда вся задача складывается в один commit, что делает историю менее читаемой.
 | ||||||
|  |                     Так же при таком подходе могут накапливаться изменения файлов при разработке. | ||||||
|  |                     Когда возникает необходимость переключиться на другую задачу, то нужно спрятать незакомиченные правки (при возвращении к задаче нужно будет снова накатить патч изменений). Этот процесс занимает время и отвлекает от задачи, уменьшает желание уходить с ветки. | ||||||
|  | 
 | ||||||
|  |                     <h3>TBD</h3> | ||||||
|  |                     При TBD нужно работать мелкими правками. Большую часть времени работаем в мастер. | ||||||
|  |                     У всех приблизительно одинаковый набор сервисов, состояние БД. | ||||||
|  |                     Небольшие различия в коде, вероятно, даже не повлияют на работоспособность окружения. | ||||||
|  |                     Если возникает необходимость переключиться на другую задачу. Нет необходимости уходить в другую ветку и пересобирать окружение. Задачу можно сделать здесь на месте. Закомитить правку и продолжить основную задачу. | ||||||
|  |                 </section> | ||||||
|  | 
 | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Release ready</h2> | ||||||
|  |                     <h3>GIT</h3> | ||||||
|  |                     Состояние Release ready достигается путем слияния feature ветки в staging и проверке на dev стенде. | ||||||
|  |                     По факту мы тестируем измененный код. Отличающийся от master и production. | ||||||
|  |                     Что может спровоцировать ряд ошибок. | ||||||
|  |                     <h3>TBD</h3> | ||||||
|  |                     Состояние Release ready достигается за счет создания release ветки и проверке на dev стенде. | ||||||
|  |                     Как и в случае с GIT flow. Мы создаем отдельную ветку и тестируем. | ||||||
|  |                     Различия в том что в случае GIT на дев стенде одновременно находится ряд задач в финальной стадии реализации, нуждающиеся в мелких правках. | ||||||
|  |                     При TBD на дев стенде будут находиться сразу все задачи принятые в работу в разных состояниях готовности. | ||||||
|  |                     В данном случае тестируется ровно тот же код что окажется в production, с теми же feature flags. Это позволяет избежать ряда ошибок и гарантировать что эту ошибку возможно воспроизвести локально, а не искать ветку в рамках которой появилась ошибка. | ||||||
|  | 
 | ||||||
|  |                     Есть ряд правил которые минимизируют количество ошибок на дев стенде | ||||||
|  |                     <ul> | ||||||
|  |                         <li> | ||||||
|  |                             <b>Маленькие атомарные комиты.</b>  | ||||||
|  |                             Позволяют легче воспринимать изменения и снижают риск ошибки. | ||||||
|  |                         </li> | ||||||
|  |                         <li> | ||||||
|  |                             <b>Continuous review</b>  | ||||||
|  |                             Теперь код не спрятан в отдельной ветке, а доступен всем. | ||||||
|  |                             Правки прилетают небольшие и каждый может их изучить. | ||||||
|  |                             Тем самым будет происходить слежение за развитием продукта и понимание что меняется | ||||||
|  |                         </li> | ||||||
|  |                         <li> | ||||||
|  |                             <b>Автотесты</b> | ||||||
|  |                         </li> | ||||||
|  |                         <li> | ||||||
|  |                             <b>Парное программирование</b>  | ||||||
|  |                             Позволяет снизить риск, того что будет допущена ошибка и спроектировать качественное архитектурное решение по задаче | ||||||
|  |                         </li> | ||||||
|  |                         <li> | ||||||
|  |                             <b>Feature flags</b>  | ||||||
|  |                             Весь не готовый код должен быть скрыт за флагом | ||||||
|  |                         </li> | ||||||
|  |                         <li> | ||||||
|  |                             Сложные задачи по прежнему можно делать в отдельных ветках | ||||||
|  |                             Важно <b>декомпозировать</b>  задачу так, чтобы работы по ней длились не более 2 дней. | ||||||
|  |                         </li> | ||||||
|  |                         <li> | ||||||
|  |                             <b>Частые деплои.</b>  | ||||||
|  |                             Уменьшает количество правок которое должно уйти в production. Следовательно и риски что-то поломать. | ||||||
|  |                         </li> | ||||||
|  |                     </ul> | ||||||
|  |                 </section> | ||||||
|  | 
 | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Схема работы по TBD</h2> | ||||||
|  |                     <div className={styles.imageWrapper}> | ||||||
|  |                         <Image src={`/trunk.png`} layout={`fill`}/> | ||||||
|  |                     </div> | ||||||
|  |                 </section> | ||||||
|  | 
 | ||||||
|  |                 <section> | ||||||
|  |                     <h2>TBD</h2> | ||||||
|  |                     <Table> | ||||||
|  |                         <TableHead> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Приемущества</TableCell> | ||||||
|  |                                 <TableCell>Недостатки</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                         </TableHead> | ||||||
|  |                         <TableBody> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Частые интеграции</TableCell> | ||||||
|  |                                 <TableCell>Сложность. Нужно понимать SOLID. Увеличивается порог входа в проект</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Постепенное изменение кода</TableCell> | ||||||
|  |                                 <TableCell>Нужно делать абстракции вокруг кода который меняется</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Возможность быстро переключиться на другую задачу</TableCell> | ||||||
|  |                                 <TableCell>Нужно написать сервисный слой для работы по TBD. Например: как использовать feature flags</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Всегда актуальный код</TableCell> | ||||||
|  |                                 <TableCell>Нужно закрывать незаконченный код через флаги, что увеличивает количество кода.</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Нет конфликтов</TableCell> | ||||||
|  |                                 <TableCell>Более качественная итоговая архитектура механизма</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Всегда одна структура бд</TableCell> | ||||||
|  |                                 <TableCell>Есть вероятность, что в проекте будут оставаться “мертвые” ветки кода.</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Уменьшение тех. долга, за счет работы с актуальной версией кода.</TableCell> | ||||||
|  |                                 <TableCell>Нужно убирать использование флагов когда код ушел в прод</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell>Нужно покрывать код тестами, легче вносить изменения.</TableCell> | ||||||
|  |                                 <TableCell>Нужно покрывать код тестами, чтобы гарантировать работоспособность.</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                             <TableRow> | ||||||
|  |                                 <TableCell></TableCell> | ||||||
|  |                                 <TableCell>Чаще будем встречать push reject</TableCell> | ||||||
|  |                             </TableRow> | ||||||
|  |                         </TableBody> | ||||||
|  |                     </Table> | ||||||
|  |                 </section> | ||||||
|  | 
 | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Branch by Abstraction</h2> | ||||||
|  |                     Можно выделить следующие этапы: | ||||||
|  |                     <ul> | ||||||
|  |                         <li>Выделить интерфейс для заменяемой функциональности.</li> | ||||||
|  |                         <li>Заменить прямой вызов реализации в клиенте на обращение к интерфейсу.</li> | ||||||
|  |                         <li>Создать новую реализацию, которая реализует интерфейс.</li> | ||||||
|  |                         <li>Подменить старую реализацию на новую.</li> | ||||||
|  |                         <li>Удалить старую реализацию.</li> | ||||||
|  |                     </ul> | ||||||
|  |                 </section> | ||||||
|  | 
 | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Пример, реализации задачи</h2> | ||||||
|  |                     <p>Имеем механизм отправки писем</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={`...
 | ||||||
|  | public function send(EmailSender $sender, array $data): void | ||||||
|  | { | ||||||
|  |     $sender->send($data); | ||||||
|  | } | ||||||
|  | ...`}
 | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p>Выделяем интерфейс</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={`interface SenderInterface {
 | ||||||
|  |   public function send(array $data): void; | ||||||
|  | } | ||||||
|  | `}
 | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p>Интегрируем его в старый механизм</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={`EmailSender implements SenderInterface`} | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p>Заменяем прямой вызов на обращение через интерфейс</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={`public function send(SenderInterface $sender, array $data): void
 | ||||||
|  | { | ||||||
|  |   $sender->send($data); | ||||||
|  | }`}
 | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p>Делаем новую реализацию</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={`SmsSender implements SenderInterface`} | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p>Заменяем вызов старого механизма на новый</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={` | ||||||
|  | public __construct(EmailSender $emailSender, SmsSender $smsSender) { | ||||||
|  |     $this->emailSender = $emailSender; | ||||||
|  |     $this->smsSender = $smsSender; | ||||||
|  | } | ||||||
|  | public function execute() { | ||||||
|  |     feature(FeatureEnum::UseSmsSender) | ||||||
|  |         ? $this->send($this->smsSender, [])  | ||||||
|  |         : $this->send($this->emailSender, []); | ||||||
|  | }`}
 | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p>либо сразу через DI</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={` | ||||||
|  | services: | ||||||
|  |     --- App\\Service\\SenderInterface: '@App\\Service\\EmailSender' | ||||||
|  |     +++ App\\Service\\SenderInterface: '@App\\Service\\SmsSender' | ||||||
|  | `}
 | ||||||
|  |                             language={`yaml`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p>и в коде будет так</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={` | ||||||
|  | public __construct( | ||||||
|  |     private EmailSender $sender | ||||||
|  | ) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public function execute() { | ||||||
|  |     $this->send($this->sender, []) | ||||||
|  | }`}
 | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                 </section> | ||||||
|  | 
 | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Feature Flags</h2> | ||||||
|  |                     <p>Суть подхода в том чтобы обернуть код в if с проверкой пред. установленной опции. | ||||||
|  |                         Например,</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={`if (feature(FeatureEnum::UseClickhouse)) {
 | ||||||
|  |   // код
 | ||||||
|  | }`}
 | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p>или</p> | ||||||
|  |                     <div className={styles.code}> | ||||||
|  |                         <CopyBlock | ||||||
|  |                             text={`feature(FeatureEnum::UseClickhouse)
 | ||||||
|  |     ? // новая логика
 | ||||||
|  |     : // старая логика
 | ||||||
|  | `}
 | ||||||
|  |                             language={`php`} | ||||||
|  |                             showLineNumbers={false} | ||||||
|  |                             startingLineNumber={1} | ||||||
|  |                             theme={github} | ||||||
|  |                         /> | ||||||
|  |                     </div> | ||||||
|  |                     <p> | ||||||
|  |                         Все флаги описываются в enum, что позволяет быстро отследить все места в проекте где флаг используется. | ||||||
|  |                         Для применения флагов на проекте, было реализовано следующее решение: | ||||||
|  |                     </p> | ||||||
|  |                     <ul> | ||||||
|  |                         <li>Сначала берутся значения из .env файлов</li> | ||||||
|  |                         <li>Далее проверяются значения в переменных окружения, например: если явно передать значение в docker</li> | ||||||
|  |                         <li>В конце проверяется значение в сессии, что позволяет быстро включить/выключить флаг и проверить функционал.</li> | ||||||
|  |                     </ul> | ||||||
|  |                     <p>Для удобного управления флагами и мониторинга состояния активности флага, на коленке была собрана служебная страница</p> | ||||||
|  |                     <div className={styles.imageWrapper}> | ||||||
|  |                         <Image src={`/feature_flags_page.png`} layout={`fill`}/> | ||||||
|  |                     </div> | ||||||
|  |                     <p>Правил с флагами будет два:</p> | ||||||
|  |                     <ul> | ||||||
|  |                         <li>После того, как функциональность полностью протестирована и стабильно работает, флаг нужно удалить.</li> | ||||||
|  |                         <li>Мест в коде, где идет ветвление по одному и тому же feature флагу, должно быть минимальное количество.</li> | ||||||
|  |                     </ul> | ||||||
|  |                 </section> | ||||||
|  |                 <section> | ||||||
|  |                     <h2>Декомпозиция задач и Short-lived ветки </h2> | ||||||
|  |                     Все ветки кода, кроме главной, должны иметь короткий срок жизни, максимум – несколько дней. | ||||||
|  |                     Этого можно добиться за счет мелкой декомпозиции: ветка будет небольшой, если она решает небольшую задачу. | ||||||
|  |                     Правильная постановка задач на этапе планирования играет очень важную роль. | ||||||
|  |                     Можно, например, придерживаемся принципа декомпозиции задач INVEST. | ||||||
|  | 
 | ||||||
|  |                     Декомпозиция по INVEST определяет, каким должен быть пользовательский сценарий (user story): | ||||||
|  |                     <ul> | ||||||
|  |                         <li>Independent — независимый</li> | ||||||
|  |                         <li>Negotiable — написанный понятным языком</li> | ||||||
|  |                         <li>Valuable — несущий ценность</li> | ||||||
|  |                         <li>Estimable — поддающийся оценке</li> | ||||||
|  |                         <li>Small — компактный, не более 40 часов разработки</li> | ||||||
|  |                         <li>Testable — тестируемый в широком смысле</li> | ||||||
|  |                     </ul> | ||||||
|  |                     Каждую из планируемых задач нужно «прогнать» по всем этим пунктам. Если по результатам выпадает хотя бы один из них, задачу нужно декомпозировать заново. | ||||||
|  |                 </section> | ||||||
|  |             </main> | ||||||
|             <footer className={styles.footer}> |             <footer className={styles.footer}> | ||||||
|         <a |                 Powered by <b>Rinsvent</b> | ||||||
|           href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app" |  | ||||||
|           target="_blank" |  | ||||||
|           rel="noopener noreferrer" |  | ||||||
|         > |  | ||||||
|           Powered by{' '} |  | ||||||
|           <span className={styles.logo}> |  | ||||||
|             <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} /> |  | ||||||
|           </span> |  | ||||||
|         </a> |  | ||||||
|             </footer> |             </footer> | ||||||
|         </div> |         </div> | ||||||
|     ) |     ) | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								public/feature_flags_page.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/feature_flags_page.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 12 KiB | 
							
								
								
									
										
											BIN
										
									
								
								public/trunk.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/trunk.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 54 KiB | 
| @ -1,5 +1,11 @@ | |||||||
| .container { | .container { | ||||||
|   padding: 0 2rem; |   padding: 0 5rem; | ||||||
|  |   font-size: 20px; | ||||||
|  |   font-family: 'Montserrat', sans-serif; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .container section { | ||||||
|  |   width: 100%; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .main { | .main { | ||||||
| @ -15,7 +21,7 @@ | |||||||
| .footer { | .footer { | ||||||
|   display: flex; |   display: flex; | ||||||
|   flex: 1; |   flex: 1; | ||||||
|   padding: 2rem 0; |   padding: 1rem 0; | ||||||
|   border-top: 1px solid #eaeaea; |   border-top: 1px solid #eaeaea; | ||||||
|   justify-content: center; |   justify-content: center; | ||||||
|   align-items: center; |   align-items: center; | ||||||
| @ -57,12 +63,15 @@ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .code { | .code { | ||||||
|   background: #fafafa; |  | ||||||
|   border-radius: 5px; |   border-radius: 5px; | ||||||
|   padding: 0.75rem; |   width: 100%; | ||||||
|   font-size: 1.1rem; |   border: #edeef0 solid 1px; | ||||||
|   font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, | } | ||||||
|     Bitstream Vera Sans Mono, Courier New, monospace; | 
 | ||||||
|  | .imageWrapper { | ||||||
|  |   position: relative; | ||||||
|  |   width: auto; | ||||||
|  |   height: 300px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .grid { | .grid { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user