Doporučené postupy v programování

Úvod, vývoj software

Lubomír Bulej

KDSS MFF UK

Otázka na úvod

Kam zařadit vývoj software?


Věda?

Řemeslo?

Umění?

Vývoj implikuje proces

Vývoj software je jako...

  • psaní (knihy, článku, dopisu)?
  • pěstování plodin?
  • pěstování ústřic (kvůli perlám)?
  • stavba (katedrály, rodinného domu, psí boudy)?
  • ...?

Metafora jako cesta...

... k pochopení procesu vývoje software

  • heuristika vs. algoritmus
  • obecný návod jak přistupovat k problémům
  • některé metafory jsou lepší než jiné

O čem bude tento předmět?

O vývoji software...

Činnosti při tvorbě softwaru - obecně

O čem bude tento předmět?

... a zejména o aktivitách, které se týkají programování ...

Činnosti při tvorbě softwaru - obecně

O čem bude tento předmět?

... tedy o psaní kódu a souvisejících činnostech.

Činnosti při tvorbě softwaru - jak je budeme probírat

Proč právě programování?

Co je na psaní kódu zvláštního?



Kód = nejdůležitější část software!

Kód = nejdůležitější část software!

V čem spočívá důležitost kódu?

  • Design se dá odbýt nebo dělat průběžně
  • Dokumentace nemusí existovat
  • Testování můžeme nechat na uživatelích

Ale kód je to, co ...

  • Nakonec běží – nedá se vynechat
  • Přesně odráží stav projektu
  • Zabere 30 – 80 % času projektu

Kód v éře AI asistentů

  • Čtení kódu zůstává náročnější než jeho psaní
  • S AI nástroji můžeme psát méně kódu, ale stále jej musíme číst/kontrolovat

Jak to souvisí s vámi?

Programování ve škole

  • složité problémy v jednoduchém kontextu
  • důraz na vědecký/matematický aspekt izolovaných problémů
  • kvalita návrhu, čistota kódu a jiné řemeslné aspekty často vedlejší (až na výjimky)

Programování ve firmě

  • jednoduché problémy ve složitém kontextu
  • v horším případě složité problémy ve složitém kontextu
  • důraz na funkčnost, udržovatelnost, a včasné dodání

Důsledkem jsou

  • problémy při realizaci složitějších projektů (semestrálky, diplomky)
  • potenciální ekonomické ztráty

Jak má tento předmět situaci zlepšit?

Náplň předmětu

  • Ukázat praktické programátorské techniky, které vedou k psaní přehlednějšího, kvalitnějšího a lépe udržovatelného kódu.
  • Vysvětlit proč je používat a na příkladech ilustrovat jejich správné použití.

Cíle předmětu

  • Schopnost navrhovat rozumné abstrakce v poslední (detailní) fázi návrhu software.
  • Schopnost rozpoznat nekvalitní kód a přeměnit jej v kvalitní.
  • Vzbudit vnitřní potřebu psát kvalitní kód, ať už proto, že je to správný způsob jak programovat, nebo proto, že se to vždy nějakým způsobem vrátí.

Tématická osnova (1/2)

Úvod

  • proč psát kvalitní kód, návrh software,
  • inherentní a zavlečená složitost.

Návrh tříd

  • návrh rozhraní (API), dědičnost vs. kompozice,
  • coupling a decoupling, modularizace a vrstvy abstrakce.

Návrh metod

  • pseudokód, lokalita vs. duplicita kódu,
  • ošetření chyb, defenzivní programování.

Tématická osnova (2/2)

Dokumentace

  • zdrojový kód, komentáře,
  • popis rozhraní, high-level dokumentace.

Psaní kódu

  • proměnné, konstanty, názvové konvence, datové typy,
  • příkazy, řídící struktury, kód řízený daty.

Zlepšování kódu

  • testování, refaktoring, ladění.

Pro koho je předmět určen?

Ideálně

  • 3. ročník Bc. studia
  • 1. ročník NMgr. studia

Méně ideálně

  • 1. ročník Bc. studia
  • 2. a vyšší ročník NMgr. studia

Organizace předmětu (1/2)

Formát 2/2, KZ

  • důraz na práci v semestru
  • cvičení dle harmonogramu na webu

Hodnocení

  • >= 87% bodů ... 1
  • >= 73% bodů ... 2
  • >= 60% bodů ... 3
  • jinak 4

Organizace předmětu (2/2)

Cvičení – Lubomír Bulej (Yours Truly)

  • zadání domácích úkolů
  • týmové prezentace API a implementace
  • code/design review, případně doplňkový materiál

Web předmětu

http://d3s.mff.cuni.cz/teaching/nprg043

  • obecné informace, slajdy z přednášek
  • harmonogram cvičení, zadání úkolů a termíny

GitLab issue tracker

https://gitlab.mff.cuni.cz/teaching/nprg043/2026-summer/forum

  • oznámení, dotazy, tvorba teamů

Programovací jazyky

Přednášky: primárně Java

  • jednoduchý, v praxi rozšířený jazyk s C-like syntaxí
  • (většinou) umožňuje zabývat se podstatou problému

Nicméně...

  • znalost jiných jazyků rozšiřuje obzory
  • použití správného nástroje na daný problém
  • dynamické jazyky: přímočaré řešení/velmi rychlé prototypování

Úkoly: C++, C#, Java (Scala)

  • týmové rozhodnutí, neškodí vyzkoušet jiný jazyk

Literatura

McConnel, S. "Code Complete", 2nd Ed.

  • vyčerpávající, dobrá argumentace, nutno více průchodů
  • Česky: "Dokonalý kód – Umění programování", 2006

Bloch, J. "Effective Java", 2nd Ed.

  • nejen o Javě – věnuje se i zásadám návrhu

Freeman, E., et al. "Head First Design Patterns", 2nd Ed.

  • návrhové principy a jejich aplikace v návrhových vzorech


+ další odkazy roztroušené po slajdech



Dotazy?

Motivační příklad...



Jak vypadá špatný kód?

"Routine from hell"


                void HandleStuff( CORP_DATA & inputRec, int crntQtr,
                   EMP_DATA empRec, double & estimRevenue, double ytdRevenue,
                   int screenX, int screenY, COLOR_TYPE & newColor,
                   COLOR_TYPE & prevColor, StatusType & status, int expenseType )
                {

                int i;
                for ( i = 0; i < 100; i++ ) {
                   inputRec.revenue[i] = 0;
                   inputRec.expense[i] = corpExpense[ crntQtr ][ i ];
                   }
                UpdateCorpDatabase( empRec );
                estimRevenue = ytdRevenue * 4.0 / (double) crntQtr;
                newColor = prevColor;
                status = SUCCESS;

                if ( expenseType == 1 ) {
                     for ( i = 0; i < 12; i++ )
                           profit[i] = revenue[i] - expense.type1[i];
                     }
                else if ( expenseType == 2 ) {
                          profit[i] = revenue[i] - expense.type2[i];
                          }

                else if ( expenseType == 3 )
                          profit[i] = revenue[i] - expense.type3[i];
                          }
            

"Routine from hell"

Jak na vás příklad působí?

  • nutkání konat násilné činy (na autorovi)...
  • kód nic moc, ale to je život, s tím se nedá nic dělat...
  • normálka, nechápu proč je kolem toho takový povyk ...

Jak špatný ten kód byl?

  • dalo by se na něm najít alespoň 15 vad
  • od formátování, přes názvy proměných a číselné konstanty, množství a uspořádání parametrů, až po fakt, že dělá řadu věcí, které spolu nesouvisí

Proč vlastně psát kvalitní kód?

Obecně se to vyplatí

  • Kód je jednou napsán, mnohokrát čten a upravován
    • "ztráta" času při psaní se vrátí nejpozději když nastanou problémy
    • problémy dříve nebo později nastanou

Technologický dluh

  • Hack je půjčka (času), jednou ji musíme vrátit (i s úroky)
    • je lepší žít bez dluhů, pokud to jde
    • příliš velké zadlužení může vést k "bankrotu"

Proč také psát kvalitní kód?

Individuální motivace

  • snaha odvést co nejlepší práci, z principu
  • vědomí, že s tím, co napíšu, budu muset žít

Tlak okolí

  • vedení (pokud rozumí programování)
  • kolegové (pokud jsou motivováni sami)
  • může být rovněž vyvíjen opačným směrem

Proč ..., když mi ho vygeneruje AI?

Posun role: autor → editor & auditor

  • Generování kódu je levné, porozumění je drahé
  • Ověření správnosti vyžaduje vyšší senioritu než jeho napsání
  • Nepřehledný (spaghetti) kód se nedá efektivně auditovat

Kód je "prompt" pro další generování

  • LLM používá váš stávající kód jako kontext a přizpůsobuje mu výstup
  • Garbage In, Garbage Out: nekonzistentní kód AI mate

AI má (také) omezený kontext

  • Kvalitní abstrakce komprimuje informace
  • Špatný kód znečišťuje kontext modelu

Ale proč hlavně psát kvalitní kód?

Zjednodušuje návrh (design)

  • kvalitní kód umožňuje zvládat složitost
  • návrh sám o sobě je dosti složitý problém

Patří návrh do programování?

  • návrh někdy dělá přímo ten, kdo píše kód
  • návrh může být pseudokód třídy
  • návrh může být pár diagramů ilustrujících vztahy mezi třídami
  • detailní návrh tříd a funkcí jde často za hranici návrhu architektury

V čem je samotný návrh tak složitý?

Při návrhu software je nutno čelit mnoha výzvám

  • návrh je zapeklitý problém (wicked)
    • k pochopení problému je nutné ho nejprve vyřešit
    • dobré řešení se od špatného příliš neliší
  • návrh je "špinavý" proces
  • návrh je o kompromisech a prioritách
  • návrh vytváří, ale hlavně omezuje možnosti
  • návrh je nedeterministický
  • návrh je výsledkem procesu

Co je při vývoji software nejdůležitější?



Zvládnutí složitosti!

Inherentní vs. zavlečená složitost

Inherentní složitost (essential complexity)

  • vlastnosti, které nějaká věc musí mít, aby byla tou věcí
  • vychází z podstaty daného problému, nelze odstranit

Zavlečená složitost (accidental complexity)

  • vlastnosti, které věc má, ale nedefinují ji (dáno řešením, ne problémem)
  • dlouhodobě se snažíme o její eliminaci (jazyk, správa paměti, formátování)

AI jako zesilovač složitosti

  • AI odstraňuje bariéru psaní ("friction of typing")
  • Umožňuje generovat zavlečenou složitost rychlostí blesku
  • Programátor jako kurátor/strážce architektury a kódu

Proč je zvládnutí složitosti tak důležité?

Velký rozsah úrovně abstrakcí

  • "od bitů po megabajty"

Omezené lidské možnosti

  • mozek dokáže najednou "udržet" pouze malý počet konceptů
  • je jednodušší "uchopit" více jednoduchých konceptů než jeden složitý

Jak udržet složitost pod kontrolou?

Vyhnout se neefektivnímu návrhu v důsledku

  • složitého řešení jednoduchého problému
  • jednoduchého, ale nesprávného řešení složitého problému
  • složitého, ale nevhodného řešení složitého problému

Počítat s tím, že inherentní složitosti se nelze vyhnout

  • Minimalizovat množství inherentní složitosti, kterou je nutné se zabývat v libovolném okamžiku
  • Globálně omezovat šíření složitosti zavlečené
    • použitím expresivnějšího jazyka, pokročilejší technologie a frameworků, odsunutím složitosti do knihoven

Příklad: omezení zavlečené složitosti

Použití indexových proměnných...


                float frubbish = 0.0;

                for (int i = 0; i < foo.length; i++) {
                  for (int j = 0; j < bar.length; j++) {
                    for (int k = 0; k < zap.length; k++) {
                      frubbish += frubbishDelta (foo[i], bar[j], zap[k]);
                    }
                  }
                }
            

Příklad: omezení zavlečené složitosti

... nahrazeno iterací s podporou v jazyce


                float frubbish = 0.0;

                for (float eachFoo : foo) {
                  for (float eachBar : bar) {
                    for (float eachZap : zap) {
                      frubbish += frubbishDelta (eachFoo, eachBar, eachZap);
                    }
                  }
                }
            

Příklad: omezení zavlečené složitosti

Transformace dat v cyklu...


                List <FieldInsnNode> result = new ArrayList <> ();
                for (AbstractInsnNode insn : Insns.asList (insns)) {
                    if (!AsmHelper.isStaticFieldAccess (insn)) {
                        continue;
                    }

                    FieldInsnNode fieldInsn = (FieldInsnNode) insn;
                    String fieldName = ThreadLocalVar.fqFieldName (insn.owner, insn.name);
                    if (!__tlvIds.contains (fieldName)) {
                        result.add (fieldInsn);
                    }
                }
            

Příklad: omezení zavlečené složitosti

Transformace dat pomocí objektových streamů...


                List <FieldInsnNode> fieldInsns = Insns.asList (insns)
                    .stream ().unordered ()
                    .filter (AsmHelper::isStaticFieldAccess)
                    .map (insn -> (FieldInsnNode) insn)
                    .filter (insn -> {
                        String fieldName = ThreadLocalVar.fqFieldNameFor (insn.owner, insn.name);
                        return tlvIds.contains (fieldName);
                    })
                    .collect (Collectors.toList ());
            

Vlastnosti dobrého návrhu software

  • Minimální možná složitost
  • Snadná údržba
  • Minimální propojenost částí (minimal coupling)
  • Rozšiřitelnost
  • Znovupoužitelnost
  • Portabilita
  • Žádné zbytečnosti
  • Stratifikace
  • Použití standardních technik/nástrojů

Five (+1) worlds

Při vývoji je nutné zohlednit typ vyvíjeného software

Základní typy software

  1. Krabicový
  2. Interní
  3. Embedded
  4. Hry
  5. Na jedno použití

  6. Software jako služba

Krabicový software

Prostředí, kde běží, není pod kontrolou

  • Nutno hodně testovat
  • Nelze spoléhat na předinstalované komponenty (knihovny)
  • Nutnost rychlého běhu (nelze upgradovat HW)
  • Konkurence: důraz na snadné používání, vzhled
  • Manažerský pohled: scalable, na vývoj lze vynaložit poměrně velké množství prostředků

Interní software

Prostředí, kde běží, je pod kontrolou

  • Stačí testovat na několika málo konfiguracích
  • Lze spoléhat na předinstalované komponenty (knihovny)
  • Lze optimalizovat upgradem HW
  • Není tlak na snadné používání, vzhled (uživatelé typicky nemají na výběr)
  • Důraz na rychlost vývoje (cyklus i v řádu týdnů a dnů)
  • Manažerský pohled: non-scalable, na vývoj nelze přidělit tolik prostředků

Embedded software

Omezené zdroje a možnost aktualizace

  • Důležitá stabilita a bezchybnost
  • V některých případech efektivita kódu důležitější než jeho čistota

Hry

Maximální využití zdrojů, omezená životnost

  • Efektivita kódu často důležitější než jeho čistota
  • Typicky jen jedna verze – důležitá stabilita a bezchybnost

Software na jedno použití

Skripty, konverze mezi datovými formáty, apod.

  • Čistota kódu nehraje téměř žádnou roli
  • Efektivita nehraje téměř žádnou roli
  • Pozor, aby se z takového software nestal produkční kód!

Software jako služba

Aplikace (typicky webové) hostované na serverech

  • Možná nejpoužívanější platforma současnosti a téměř určitě budoucnosti
    • Než začnete psát jakoukoliv aplikaci, položte si otázku, zda nebude lepší ji napsat jako webovou.
  • Samozřejmě se nejedná o platformu univerzální:
    • Odezva GUI (hry, práce s grafikou)
    • Velká množství dat (grafické programy)
    • Bezpečnost (např. účetní program)
  • Svoboda volby OS, platformy, jazyka

Software jako služba

Proč psát software jako službu?

  • Extrémně snadný update všech uživatelů najednou
    • Ze zvyšování kvality mají užitek všichni uživatelé
    • Žádná starost o zpětnou kompatibilitu, staré formáty dat, apod.
    • Jednoznačná preference inkrementálního vývoje
    • Možnost okamžitě opravit chyby, které se navíc snadno reprodukují

      "Often I could fix the code and release a fix right away. And when I say right away, I mean while the user was still on the phone." – Paul Graham

  • Snadné monitorování chování uživatelů

Software jako služba

  • U větších aplikací: "City of code"
    • Spousta programů, co spolu spolupracují
    • Typicky v různých jazycích
    • Vlastní/cizí

    "As well as buildings you need roads, street signs, utilities, police and fire departments, and plans for both growth and various kinds of disasters." –Paul Graham