paint-brush
Jak se vypořádat se složitostí při navrhování softwarových systémůpodle@fairday
64,370 čtení
64,370 čtení

Jak se vypořádat se složitostí při navrhování softwarových systémů

podle Aleksei23m2024/02/05
Read on Terminal Reader
Read this story w/o Javascript

Příliš dlouho; Číst

Složitost je nepřítel! Pojďme se naučit, jak se s tím vypořádat!
featured image - Jak se vypořádat se složitostí při navrhování softwarových systémů
Aleksei HackerNoon profile picture

o co jde?

Každý den, každou chvíli během naší inženýrské kariéry narážíme na mnoho různých problémů různé složitosti a situací, kdy potřebujeme učinit rozhodnutí nebo je odložit z důvodu nedostatku dat. Kdykoli budujeme nové služby, budujeme infrastrukturu nebo dokonce formujeme vývojové procesy, dotýkáme se obrovského světa různých výzev.


Je náročné a možná i nemožné vyjmenovat všechny problémy. S některými z těchto problémů se setkáte pouze tehdy, pokud pracujete v konkrétním výklenku. Na druhou stranu je mnoho věcí, kterým musíme všichni rozumět, protože jsou klíčové pro budování IT systémů. S velkou pravděpodobností se s nimi setkáte ve všech projektech.


V tomto článku se podělím o své zkušenosti s některými problémy, se kterými jsem se setkal při vytváření softwarových programů.

Co je to průřezový zájem?

Pokud se podíváme do Wikipedie, najdeme následující definici


V aspektově orientovaném vývoji softwaru jsou průřezové problémy aspekty programu, které ovlivňují několik modulů, aniž by bylo možné je zapouzdřit do některého z nich. Tyto obavy často nelze čistě rozložit od zbytku systému jak při návrhu, tak při implementaci a mohou mít za následek buď rozptýlení (duplikace kódu), zamotání (významné závislosti mezi systémy) nebo obojí.


Velmi to popisuje, co to je, ale chci to trochu rozšířit a zjednodušit:

Průřezový problém je koncept nebo složka systému/organizace, která ovlivňuje (nebo „prostupuje“) mnoho dalších částí.


Nejlepšími příklady takových problémů jsou architektura systému, protokolování, bezpečnost, správa transakcí, telemetrie, návrh databází a existuje mnoho dalších. Mnohým z nich se budeme věnovat dále v tomto článku.


Na úrovni kódu jsou průřezové problémy často implementovány pomocí technik, jako je AOP (Aspect-Oriented Programming) , kde jsou tyto problémy modularizovány do samostatných komponent, které lze aplikovat v celé aplikaci. To udržuje obchodní logiku izolovanou od těchto problémů, takže kód je čitelnější a udržitelnější.

Klasifikace aspektů

Existuje mnoho možných způsobů, jak klasifikovat aspekty jejich segmentací podle různých vlastností, jako je rozsah, velikost, funkčnost, důležitost, cíl a další, ale v tomto článku použiji jednoduchou klasifikaci rozsahu. Tím mám na mysli to, kam tento specifický aspekt směřuje, ať už se jedná o celou organizaci, konkrétní systém nebo konkrétní prvek tohoto systému.


Takže rozdělím aspekty na makro a mikro .


Makro aspektem mám na mysli především úvahy, kterými se řídíme pro celý systém, jako je zvolená architektura systému a jeho návrh (monolitický, mikroslužby, architektura orientovaná na služby), technologický stack, organizační struktura atd. Makro aspekty se týkají především strategické a vysoké úrovně rozhodnutí.


Mezitím je aspekt Micro mnohem blíže úrovni kódu a vývoji. Například, který rámec se používá pro interakci s databází, struktura projektu složek a tříd nebo dokonce konkrétní vzory návrhu objektů.


I když tato klasifikace není ideální, pomáhá strukturovat chápání možných problémů a důležitosti a dopadu řešení, která na ně aplikujeme.


V tomto článku se zaměřím především na makro aspekty.

Makro aspekty

Organizační struktura

Když jsem se teprve začal učit o softwarové architektuře, četl jsem mnoho zajímavých článků o Conwayově zákoně a jeho dopadu na organizační strukturu. Zvláště tento . Tak to říká tento zákon


Každá organizace, která navrhuje systém (definovaný široce), vytvoří návrh, jehož struktura je kopií komunikační struktury organizace.


Vždy jsem věřil, že tento koncept je skutečně velmi univerzální a představuje Zlaté pravidlo.


Poté jsem se začal učit přístup Erica Evanse Domain-Driven Design (DDD) pro modelování systémů. Eric Evans zdůrazňuje důležitost identifikace ohraničeného kontextu. Tento koncept zahrnuje rozdělení komplexního doménového modelu na menší, lépe ovladatelné části, z nichž každá má svůj vlastní omezený soubor znalostí. Tento přístup napomáhá efektivní týmové komunikaci, protože snižuje potřebu rozsáhlých znalostí celé domény a minimalizuje přepínání kontextu, takže konverzace jsou efektivnější. Přepínání kontextu je nejhorší a na zdroje vůbec nejnáročnější věc. Dokonce i počítače se s tím potýkají. I když je nepravděpodobné, že bychom dosáhli úplné absence přepínání kontextu, domnívám se, že o to bychom měli usilovat.


Fantasy about keeping in mind a lot of bounded contexts

Když se vrátím ke Conwayovu zákonu, našel jsem v něm několik problémů.


První problém, se kterým jsem se setkal s Conwayovým zákonem, který naznačuje, že systémový návrh odráží organizační strukturu, je potenciál pro vytváření složitých a komplexních ohraničených kontextů. Tato složitost vzniká, když organizační struktura není v souladu s hranicemi domén, což vede k ohraničeným kontextům, které jsou na sobě silně závislé a zatížené informacemi. U vývojového týmu to vede k častému přepínání kontextu.


Dalším problémem je únik organizační terminologie na úroveň kódu. Když se organizační struktury změní, vyžaduje to úpravy kódové základny, což spotřebovává cenné zdroje.


Následování Inverse Conway Maneuver tedy pomáhá vybudovat systém a organizaci, které podporují požadovanou softwarovou architekturu. Je však pozoruhodné říci, že tento přístup nebude velmi dobře fungovat v již vytvořené architektuře a strukturách, protože změny v této fázi jsou dlouhé, ale výjimečně funguje ve startupech, protože rychle zavádějí jakékoli změny.

Velká koule bahna

Tento vzor nebo „anti-vzor“ pohání budování systému bez jakékoli architektury. Neexistují žádná pravidla, žádné hranice a žádná strategie, jak kontrolovat nevyhnutelnou rostoucí složitost. Složitost je nejhrozivějším nepřítelem na cestě budování softwarových systémů.


Entertaining illustration made by ChatGPT

Abychom se vyhnuli konstrukci takového typu systému, musíme dodržovat určitá pravidla a omezení.

Architektura systému

Existuje nespočet definic pro softwarovou architekturu. Líbí se mi mnoho z nich, protože pokrývají různé aspekty. Abychom však mohli uvažovat o architektuře, musíme si některé z nich přirozeně vytvořit v mysli. A je pozoruhodné říci, že tato definice se může vyvíjet. Pro sebe mám tedy alespoň prozatím následující popis.


Softwarová architektura je o rozhodnutích a volbách, které činíte každý den a které mají dopad na vybudovaný systém.


Chcete-li se rozhodovat, musíte mít ve své „tašce“ principy a vzorce řešení vznikajících problémů, je také nezbytné uvést, že pochopení požadavků je klíčem k budování toho, co podnik potřebuje. Někdy však požadavky nejsou transparentní nebo dokonce nejsou definovány, v tomto případě je lepší počkat na další vysvětlení nebo se spolehnout na své zkušenosti a věřit své intuici. Ale stejně se nemůžete správně rozhodovat, pokud nemáte zásady a vzorce, na které se můžete spolehnout. Zde se dostávám k definici stylu softwarové architektury.


Styl softwarové architektury je soubor principů a vzorů, které určují, jak vytvářet software.


Existuje mnoho různých architektonických stylů zaměřených na různé stránky plánované architektury a použití více z nich najednou je normální situace.


Například jako:

  1. Monolitická architektura

  2. Design řízený doménou

  3. Na bázi komponent

  4. Mikroslužby

  5. Potrubí a filtry

  6. Událostí

  7. Mikrokernel

  8. Zaměřené na služby


a tak dále…


Samozřejmě mají své výhody a nevýhody, ale nejdůležitější věc, kterou jsem se naučil, je, že architektura se vyvíjí postupně a závisí na skutečných problémech. Počínaje monolitickou architekturou je skvělou volbou pro snížení provozních složitostí, velmi pravděpodobně bude tato architektura vyhovovat vašim potřebám i po dosažení fáze Product-market Fit (PMI) při vytváření produktu. Ve velkém měřítku můžete zvážit přechod na událostmi řízený přístup a mikroslužby pro dosažení nezávislého nasazení, heterogenního prostředí technologického zásobníku a méně propojené architektury (a mezitím méně transparentní kvůli povaze událostí řízených a pub-sub přístupů, pokud tyto jsou přijaty). Jednoduchost a efektivita jsou si blízké a mají na sebe velký vliv. Složité architektury obvykle ovlivňují rychlost vývoje nových funkcí, podporují a udržují ty stávající a zpochybňují přirozený vývoj systému.


Složité systémy však často vyžadují komplexní a komplexní architekturu, což je nevyhnutelné.


Popravdě, toto je velmi velmi široké téma a existuje mnoho skvělých nápadů, jak strukturovat a budovat systémy pro přirozenou evoluci. Na základě svých zkušeností jsem vypracoval následující postup:

  1. Téměř vždy začíná monolitickým stylem architektury, protože odstraňuje většinu problémů, které vznikají v důsledku povahy distribuovaných systémů. Má také smysl sledovat modulární monolit a zaměřit se na stavební komponenty s jasnými hranicemi. Použití přístupu založeného na komponentách by jim mohlo pomoci komunikovat mezi sebou pomocí událostí, ale přímé volání (neboli RPC) zjednodušuje věci na začátku. Je však důležité sledovat závislosti mezi komponentami, protože pokud komponenta A ví hodně o komponentě B, možná má smysl je sloučit do jedné.
  2. Když se přiblížíte situaci, kdy potřebujete škálovat svůj vývoj a systém, můžete zvážit následování vzoru Stangler a postupně extrahovat komponenty, které je třeba nasadit nezávisle nebo dokonce škálovat podle specifických požadavků.
  3. Nyní, pokud máte jasnou vizi budoucnosti, což je trochu neuvěřitelné štěstí, můžete se rozhodnout pro požadovanou architekturu. V tuto chvíli byste se mohli rozhodnout o přechodu na architekturu mikroslužeb také tím, že použijete přístupy Orchestrace a Choreography, začleníte vzor CQRS pro operace zápisu a čtení s nezávislým měřítkem, nebo se dokonce rozhodnete zůstat u monolitické architektury, pokud to vyhovuje vašim potřebám.


Je také důležité porozumět číslům a metrikám, jako jsou DAU (denní aktivní uživatelé), MAU (měsíční aktivní uživatelé), RPC (požadavek za sekundu) a TPC (transakce za sekundu), protože vám to může pomoci při rozhodování, protože architektura pro 100 aktivních uživatelů a 100 milionů aktivních uživatelů se liší.


Jako poslední poznámku bych řekl, že architektura má významný vliv na úspěch produktu. Špatně navržená architektura produktů je vyžadována při škálování, což velmi pravděpodobně vede k selhání, protože zákazníci nebudou čekat, až systém škálujete, ale vyberou si konkurenta, takže musíme být před potenciálním škálováním napřed. I když uznávám, že někdy to nemůže být štíhlý přístup, myšlenkou je mít škálovatelný, ale ještě neškálovaný systém. Na druhou stranu, mít velmi komplikovaný a již škálovaný systém bez zákazníků nebo plánů získat mnoho z nich vás bude stát peníze na vašem podnikání za nic.

Výběr technologického zásobníku

Výběr technologického zásobníku je také rozhodnutí na makroúrovni, protože ovlivňuje nábor, perspektivy přirozeného vývoje systému, škálovatelnost a výkon systému.


Toto je seznam základních úvah při výběru technologického zásobníku:

  • Požadavky a složitost projektu. Například jednoduchou webovou aplikaci lze sestavit s rámcem Blazor, pokud s ním vaši vývojáři mají zkušenosti, ale kvůli nedostatečné vyspělosti WebAssembly by pro dlouhodobý úspěch mohlo být lepším rozhodnutím zvolit React a Typescript.
  • Škálovatelnost a požadavky na výkon. Pokud očekáváte velké množství provozu, volba ASP.NET Core přes Django by mohla být rozumnou volbou kvůli jeho vynikajícímu výkonu při zpracování souběžných požadavků. Toto rozhodnutí však závisí na rozsahu provozu, který očekáváte. Pokud potřebujete spravovat potenciálně miliardy požadavků s nízkou latencí, může být přítomnost Garbage Collection výzvou.
  • Nájem, doba vývoje a náklady. Ve většině případů jsou to faktory, o které se musíme starat. Čas uvedení na trh, náklady na údržbu a stabilita náboru řídí vaše obchodní potřeby bez překážek.
  • Odbornost týmu a zdroje. Klíčovým faktorem jsou dovednosti vašeho vývojového týmu. Obecně je efektivnější používat technologie, které váš tým již zná, pokud neexistuje pádný důvod investovat do učení nového zásobníku.
  • Zralost. Silná komunita a bohatý ekosystém knihoven a nástrojů mohou výrazně usnadnit proces vývoje. Populární technologie mají často lepší podporu komunity, což může být neocenitelné při řešení problémů a hledání zdrojů. Můžete tak ušetřit zdroje a soustředit se hlavně na produkt.
  • Dlouhodobá údržba a podpora. Zvažte dlouhodobou životaschopnost technologie. Technologie, které jsou široce přijímány a podporovány, jsou méně pravděpodobné, že zastarají a obecně dostávají pravidelné aktualizace a vylepšení.


Jak může mít více technologických zásobníků vliv na obchodní růst?

Z jedné perspektivy by zavedení jednoho dalšího balíčku mohlo zvýšit váš nábor, ale na druhou stranu to přináší další náklady na údržbu, protože musíte podporovat oba balíčky. Takže, jak jsem řekl dříve, z mého pohledu by argumentem pro začlenění více technologických zásobníků měla být pouze další potřeba.


Jak je to ale s principem výběru nejlepšího nástroje pro konkrétní problém?

Někdy nemáte jinou možnost, než přinést nové nástroje k řešení konkrétního problému na základě stejných úvah, jako je uvedeno výše, v takových případech má smysl vybrat to nejlepší řešení.


Vytvoření systémů bez vysoké vazby na konkrétní technologii může být výzvou. Přesto je užitečné usilovat o stav, kdy systém není pevně propojen s technologií, a nezemře, pokud se zítra konkrétní rámec nebo nástroj stane zranitelným nebo dokonce zastaralým.


Další důležitá úvaha souvisí se závislostmi open-source a proprietárního softwaru. Proprietární software vám poskytuje menší flexibilitu a možnost přizpůsobení. Nejnebezpečnějším faktorem je však uzamčení dodavatele, kdy se stáváte závislými na produktech, cenách, podmínkách a cestovní mapě dodavatele. To může být riskantní, pokud prodejce změní směr, zvýší ceny nebo ukončí výrobu produktu. Open-source software toto riziko snižuje, protože jej nekontroluje jediný subjekt. Odstranění jediného bodu selhání na všech úrovních je klíčem k vybudování spolehlivých systémů pro růst.

Jediný bod selhání (SPOF)

Jediný bod selhání (SPOF) označuje jakoukoli část systému, která, pokud selže, způsobí, že celý systém přestane fungovat. Eliminace SPOF na všech úrovních je zásadní pro jakýkoli systém vyžadující vysokou dostupnost. Všechno, včetně znalostí, personálu, součástí systému, poskytovatelů cloudu a internetových kabelů, může selhat.


Existuje několik základních technik, které můžeme použít k odstranění jednotlivých bodů selhání:

  1. Redundance. Implementujte redundanci pro kritické komponenty. To znamená mít záložní komponenty, které mohou převzít kontrolu, pokud primární komponenta selže. Redundanci lze aplikovat napříč různými vrstvami systému, včetně hardwaru (servery, disky), sítí (odkazy, přepínače) a softwaru (databáze, aplikační servery). Pokud vše hostujete u jednoho poskytovatele cloudu a dokonce tam máte zálohy, zvažte vytvoření pravidelné dodatečné zálohy v jiném, abyste snížili své ztracené náklady v případě katastrofy.
  2. datová centra. Distribuujte svůj systém na více fyzických místech, jako jsou datová centra nebo cloudové oblasti. Tento přístup chrání váš systém proti poruchám specifickým pro dané místo, jako jsou výpadky proudu nebo přírodní katastrofy.
  3. Failover. Použijte přístup převzetí služeb při selhání pro všechny vaše komponenty (DNS, CDN, nástroje pro vyrovnávání zatížení, Kubernetes, brány API a databáze). Vzhledem k tomu, že problémy mohou nastat neočekávaně, je důležité mít plán zálohování, který podle potřeby rychle nahradí jakoukoli součást svým klonem.
  4. Služby vysoké dostupnosti. Zajistěte, aby byly vaše služby od začátku horizontálně škálovatelné a vysoce dostupné, a to dodržením následujících zásad:
    • Procvičte si bezstavovou službu a vyhněte se ukládání uživatelských relací do mezipaměti v paměti. Místo toho použijte distribuovaný systém mezipaměti, jako je Redis.
    • Při vývoji logiky se vyvarujte spoléhání se na chronologické pořadí spotřeby zpráv.
    • Minimalizujte změny narušení, abyste zabránili narušení uživatelů rozhraní API. Kde je to možné, zvolte zpětně kompatibilní změny. Zvažte také náklady, protože někdy může být implementace zásadní změny nákladově efektivnější.
    • Zahrňte provádění migrace do kanálu nasazení.
    • Stanovte strategii pro zpracování souběžných požadavků.
    • Implementujte zjišťování, monitorování a protokolování služeb pro zvýšení spolehlivosti a pozorovatelnosti.
    • Rozvíjejte obchodní logiku tak, aby byla idempotentní a uznávala, že selhání sítě jsou nevyhnutelná.
  5. Kontrola závislosti. Pravidelně kontrolujte a minimalizujte externí závislosti. Každá externí závislost může představovat potenciální SPOF, takže je nezbytné těmto rizikům porozumět a zmírnit je.
  6. Pravidelné sdílení znalostí. Nikdy nezapomínejte na důležitost šíření znalostí ve vaší organizaci. Lidé mohou být nepředvídatelní a spoléhat se na jednoho jedince je riskantní. Povzbuďte členy týmu, aby digitalizovali své znalosti prostřednictvím dokumentace. Dejte si však pozor na přílišnou dokumentaci. Pro zjednodušení tohoto procesu použijte různé nástroje AI.

Závěr

V tomto článku jsme se zabývali několika klíčovými makro aspekty a jak se můžeme vypořádat s jejich složitostí.


Děkuji za přečtení! Uvidíme se příště!