Mérnöki kézikönyv · 01 / 09 · Mérnöki módszertan

A papírkorszak programozási módszertanai

Fejlesztőknek, CTO-knak és AI projektet vezető szakembereknek szól, akik a másodpercek alatt generált kód mellé megbízható ellenőrzési fegyelmet keresnek. Huszonegy klasszikus módszer lépésről lépésre, tipikus hibákkal és a mai, AI asszisztált alkalmazásukkal, a végén egy közvetlenül használható szabálykészlettel.

Kalmár Dániel · kb. 68 perc olvasás · A sorozat része, Mérnöki kézikönyvek

Tartalom

A kiinduló helyzet

1960 körül egy átlagos programozó napja így telt. Kódolólapra ceruzával megírta a programot, a lapot leadta a lyukasztóknak, a kártyacsomag egy tálcában várt a gépteremben, és a futás eredményét (gyakran egy fordítási hibalistát) másnap reggel kapta vissza leporellón. Egy elírás egy elveszett nap volt, egy logikai hiba egy elveszett hét. Batch feldolgozásnál a fordulási idő (turnaround) órákban és napokban mérődött.

Ebből a kényszerből született a korszak egész módszertani kultúrája. Ha a gép drága és ritka, a gondolkodást kell olcsóvá és alapossá tenni. A program helyességét papíron, fejben, kollégák szeme előtt kellett megalapozni, mielőtt a gép egyáltalán meglátta. Vibecodingnál fordított a helyzet. Gépidő szinte ingyen van, a generálás másodperc, ezért a gondolkodás maradt az egyetlen szűk keresztmetszet. A papírkorszak eszközei ma azért értékesek, mert pont ezt a szűk keresztmetszetet kezelik.


1. Kódolólap, asztali ellenőrzés és kézi végrehajtás

A kódolólap

A kódolólap (coding sheet, coding form) előre nyomtatott rácsos papír volt, amelynek oszlopai a lyukkártya 80 oszlopát követték. FORTRAN lapon az 1 és 5 közötti oszlop a címke (statement number), a 6. a folytatásjel, a 7 és 72 közötti az utasítás, a 73 és 80 közötti a sorszám vagy azonosító. COBOL lapon az 1 és 6 közötti oszlop a sorszám, a 7. a megjegyzés vagy folytatás jele, a 8 és 11 közötti az A terület (division, section, paragrafus nevek), a 12 és 72 közötti a B terület.

A kódolólap fegyelmet kényszerített ki. Minden karakternek helye volt, a nulla és az O betű közti különbséget áthúzással jelölték (a lyukasztó nem tudta kitalálni), a sorszámozás lehetővé tette, hogy egy leejtett kártyacsomagot géppel újrarendezzenek. A programozó tehát egy fizikai, sorrendezett, karakterhű dokumentumot írt, amelyet más ember gépelt be.

Mai megfelelő. A promptban vagy specifikációban rögzített formátum. Ha egy fájl formátuma kötött (CSV oszlopsorrend, fix szélességű rekord, JSON séma, SQL migráció), a kódgenerálás előtt leírjuk a formátum rácsát (mezőnév, típus, kötelező-e, példaérték), és csak utána készül hozzá kód.

McCracken és a korabeli tankönyvek

Daniel D. McCracken könyvei voltak a korszak legolvasottabb programozási tankönyvei. Digital Computer Programming (1957), A Guide to FORTRAN Programming (1961), A Guide to COBOL Programming (1963, Umberto Garbassival), A Guide to FORTRAN IV Programming (1965). Ezek nem nyelvi referenciák voltak, hanem munkamódszert tanítottak. A tipikus lecke egy feladatleírással indult, utána folyamatábra következett, ebből lett a kódolólapra írt program, majd a kézi ellenőrzés és végül a gépi futás kimenete, sokszor a hibákkal együtt.

A McCracken iskola munkarendje lépésenként.

  1. A feladat megfogalmazása saját szavakkal, a bemenetek és kimenetek felsorolásával.
  2. Folyamatábra a megoldásról, előbb nagy blokkokkal, majd részletezve.
  3. A változók és tömbök listája, méretekkel és jelentéssel.
  4. A kód megírása a kódolólapra a folyamatábra alapján.
  5. Asztali ellenőrzés kitalált, de kézzel kiszámolt bemenetekkel.
  6. Tesztadatok előkészítése, amelyeknél az eredményt előre ismerjük.
  7. Gépi futtatás, majd a kimenet összevetése az előre kiszámolt eredménnyel.

A 6. lépés a lényeg, amit ma is elhagynak. A tesztadat elvárt eredményét a futás előtt számolták ki kézzel, így a gépi kimenet nem tudta befolyásolni az ítéletet.

Asztali ellenőrzés (desk checking)

Az asztali ellenőrzés a program kézi átvizsgálása gép nélkül, a programozó saját asztalánál. Két rétege van. A statikus réteg a szöveg átnézése (szintaxis, deklarációk, típusok, címkék, határok), a dinamikus réteg a program kézi végrehajtása konkrét bemenettel.

Lépésenként így megy.

  1. Szintaktikai kör. Soronként végigolvasás. Minden zárójel párban van-e, minden címke létezik-e, amire ugrás mutat, minden változó kap-e értéket, mielőtt olvassuk.
  2. Deklarációs kör. Minden változóhoz és tömbhöz egy sor (név, típus, méret, jelentés). Ami nincs a listán, az elírás.
  3. Határkör. Minden ciklusnál és tömbindexnél a legkisebb és a legnagyobb érték kiszámítása. A tömbhatár túllépése és az eggyel elcsúszott ciklus (off-by-one) itt derül ki.
  4. Végrehajtási kör. Nyomkövető táblával (következő alpont) legalább három bemenetre. Egy tipikus esetre, egy határesetre (üres, egy elemű, maximális) és egy hibás bemenetre.
  5. Kimeneti kör. A kézzel kapott eredmény összevetése az elvárt eredménnyel.

Mikor érdemes ma. Minden olyan kódrésznél, ahol a hiba csendes (rossz számot ad, nem összeomlik). Pénzügyi kerekítés, dátumszámítás, lapozás, jogosultságellenőrzés, ciklus egy adathalmaz szélén.

Tipikus hiba. Csak a tipikus esetet követik végig, mert az a legkényelmesebb. A hibák túlnyomó része a határeseteknél van.

Nyomkövető tábla (trace table)

A nyomkövető tábla a kézi végrehajtás jegyzőkönyve. Oszlopai a program változói (és gyakran a vizsgált feltételek, valamint a kimenet), sorai a végrehajtás lépései. Minden értékadásnál új érték kerül a megfelelő oszlopba.

Példa. Egy függvény, amely egy rendezett listában bináris kereséssel keres.

def binsearch(a, x):
    lo, hi = 0, len(a) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if a[mid] == x:
            return mid
        elif a[mid] < x:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

Kézi végrehajtás a = [2, 5, 8, 12, 16], x = 16 bemenettel.

Lépéslohilo <= himida[mid]ÁgKimenet
init04
104igaz288 < 16, lo = 3
234igaz31212 < 16, lo = 4
344igaz416egyenlő4

Második bemenet a hiányzó elemre, x = 1.

Lépéslohilo <= himida[mid]ÁgKimenet
init04
104igaz288 > 1, hi = 1
201igaz022 > 1, hi = -1
30-1hamiskilépés-1

Harmadik bemenet az üres listára. hi = -1, a ciklus nem indul, kimenet -1. A három tábla együtt lefedi a megtalált, a hiányzó és a degenerált esetet. Ha a ciklusfeltétel lo < hi lenne, az első táblázat 3. lépésében, a lo = hi = 4 állapotban a ciklus már nem futna le, és a függvény a megtalálható elem helyett -1 értéket adna. A nyomkövető tábla ezt a hibát gép nélkül kimutatja.

Jó gyakorlat.

Mai alkalmazás. Kritikus ciklusnál (lapozás, kötegelés, összesítés, újrapróbálás számlálóval, ablakos feldolgozás) a generált kód mellé egy rövid nyomkövető tábla kerül két-három bemenetre. Ez gyorsabb, mint egy teszt megírása. Ha a táblát a kódgeneráló modell tölti ki egy külön lépésben, a saját tévedései is felszínre kerülnek, mert a kitöltés közben a valódi szemantikát kell követni, nem a kód hangulatát. Eltérés esetén a táblázat helyett a kód a gyanús.

A gondolat gyökerei

Az elv régebbi a lyukkártyánál. Herman Goldstine és Neumann János 1947 és 1948 közötti jelentései (Planning and Coding of Problems for an Electronic Computing Instrument) már folyamatábrát (flow diagram) és a diagram egyes pontjaihoz rendelt állításokat (assertion box) használtak. Alan Turing 1949-es rövid előadása (Checking a Large Routine) azt javasolta, hogy a programozó a program bizonyos pontjain rögzítse, mi igaz az állapotról, így az ellenőrző személy ezeket egyenként nézheti meg. Floyd és Hoare később ebből építette fel a formális helyességbizonyítást.


2. Folyamatábra és rokonai

ANSI és ISO szimbólumok

Az első szabvány az ANSI X3.5 volt (1970), ezt követte az ISO 1028 (1973), majd a ma is érvényes ISO 5807:1985. A legfontosabb szimbólumok.

SzimbólumAlakJelentés
Terminátorlekerekített téglalap (ovális)Kezdet, vég, megszakítás
FolyamattéglalapEgy vagy több művelet, értékadás
DöntésrombuszFeltétel, két vagy több kimenő ág, feliratozott ágakkal
Adat (I/O)paralelogrammaBemenet vagy kimenet, általánosan
Előre definiált folyamattéglalap két függőleges belső vonallalMáshol részletezett alprogram
ElőkészítéshatszögCiklus inicializálás, kapcsoló beállítása
Kézi bevitelferde tetejű téglalapBillentyűzetről beírt adat
Dokumentumhullámos aljú téglalapNyomtatott kimenet
Csatlakozókis körUgyanazon a lapon folytatódó ág
Lapon kívüli csatlakozóötszög (házikó)Másik lapon folytatódó ág
FolyamatvonalnyílA vezérlés iránya, alapértelmezés fentről le és balról jobbra

A jó folyamatábra szabályai.

  1. Egy bejárat és egy kijárat a teljes ábrán.
  2. Minden döntésnél minden kimenő ág feliratozva (igen és nem, vagy a lehetséges értékek).
  3. Egy lap egy absztrakciós szint. Ha egy blokk részletezése nem fér el, előre definiált folyamat szimbólum kerül a helyére, és külön lapon van kifejtve.
  4. A ciklus visszatérő vonala jól látható, és a ciklus kilépési feltétele egyetlen rombuszban szerepel.
  5. A vonalak nem keresztezik egymást. Ha muszáj, csatlakozó kör jön.

Tipikus hiba. A folyamatábra a goto logikát rajzolja le, és tetszőleges pontok között enged nyilat húzni. Ebből lesz a spagetti ábra, amelyből spagetti kód születik. Dijkstra strukturált programozása után a jó folyamatábra csak a három alapszerkezetet (szekvencia, elágazás, ciklus) tartalmazza, egymásba ágyazva.

Nassi és Shneiderman struktogram (1973)

Isaac Nassi és Ben Shneiderman 1973-ban egy olyan diagramot javasolt (Flowchart Techniques for Structured Programming, SIGPLAN Notices), amelyben nyíl egyáltalán nincs. A program egy téglalap, amelyet vízszintesen szekvenciális blokkokra, az elágazásnál háromszöggel két oszlopra, a ciklusnál L alakú kerettel osztanak. Goto egyszerűen nem rajzolható le. Közép-Európában, így Magyarországon is struktogram néven sokáig ez volt a középiskolai és egyetemi algoritmusoktatás alapja.

Pszeudokód és PDL

Stephen Caine és Kent Gordon 1975-ben írta le a PDL (Program Design Language) nyelvet. A gondolat az, hogy a tervezést természetes nyelvű, de rögzített kulcsszavakkal (IF, THEN, ELSE, DO WHILE, ENDDO) strukturált szövegben végezzük, amely közvetlenül kóddá finomítható.

Mai alkalmazás. Bonyolult vezérlési logikánál (állapotgép, többlépéses jóváhagyási folyamat, újrapróbálás visszalépéssel) előbb egy Mermaid flowchart vagy stateDiagram ábra vagy rövid pszeudokód készül, ezt a fejlesztő jóváhagyja, és csak ezután jön a kódgenerálás. Az ábra szabálya ugyanaz, mint 1975-ben. Minden döntés minden ága feliratozott, és minden állapotból van út a végállapotba.

flowchart TD
  A([Webhook érkezik]) --> B{Aláírás érvényes?}
  B -- nem --> X([401, naplózás])
  B -- igen --> C{Esemény ID már feldolgozva?}
  C -- igen --> Y([200, nincs teendő])
  C -- nem --> D[Feldolgozás tranzakcióban]
  D --> E[Esemény ID mentése]
  E --> Z([200])

3. Döntési táblák

Eredet

A döntési táblát az 1950-es évek végén több helyen használták (General Electric, Sutherland Corporation), a szabványosítást a CODASYL Decision Tables Task Group végezte az 1960-as évek elején. Az első DETAB-X előfeldolgozó (1962 körül) döntési táblából COBOL kódot generált. A korszakban a döntési tábla a bonyolult üzleti szabályok (biztosítási díjszámítás, bérszámfejtés, hitelbírálat) elsődleges specifikációs eszköze volt, és a DeMarco féle strukturált elemzés is átvette a mini specifikációk egyik formájaként.

Felépítés

A tábla négy negyedből áll.

Szabályok
Feltételcsonk (condition stub). A feltételek felsorolásaFeltételbejegyzések (condition entries). Az egyes szabályok I, N vagy - értékei
Akciócsonk (action stub). A lehetséges akciókAkcióbejegyzések (action entries). X, ha az akció az adott szabálynál végrehajtódik

A korlátozott bejegyzésű (limited entry) táblában a feltételek igen vagy nem értéket vehetnek fel, a kiterjesztett bejegyzésűben (extended entry) a bejegyzés maga is érték (például kategória).

Lépések

  1. Feltételek összegyűjtése. Minden olyan tény, amely a döntést befolyásolja, egy sor. Csak egymástól független, eldönthető feltételek kerülnek bele.
  2. Akciók összegyűjtése. Minden lehetséges kimenet egy sor, a sorrend a végrehajtási sorrend.
  3. Szabályszám kiszámítása. n darab kétértékű feltételnél 2^n oszlop. Három feltételnél 8, négynél 16.
  4. A teljes tábla kitöltése. Az első feltételsor felében I, felében N, a második negyedenként váltakozik, és így tovább. Így minden kombináció egyszer és csak egyszer szerepel.
  5. Akciók hozzárendelése minden oszlophoz.
  6. Összevonás. Ha két oszlop csak egy feltételben tér el, és az akcióik azonosak, az a feltétel nem számít, egy oszlop lesz belőle - (közömbös) jellel.
  7. Ellenőrzés. Teljesség (a közömbös jelek kibontásával a szabályok száma 2^n), ellentmondásmentesség (két átfedő oszlop nem ír elő eltérő akciót), redundancia (két oszlop nem fedi ugyanazt).
  8. ELSE oszlop a lehetetlennek ítélt kombinációkra, explicit hibaakcióval, nem csendes elhagyással.

Példa

Szállítási díj szabálya egy webshopban. Feltételek. A rendelés értéke legalább 20 000 Ft, a vevő prémium tag, a cím külföldi.

Feltétel12345678
Érték >= 20 000IIIINNNN
Prémium tagIINNIINN
Külföldi címININININ
Akció
Ingyenes szállításXXX
Belföldi díjX
Külföldi díjXXXX

Összevonás után látszik, hogy a külföldi cím mindig külföldi díjat jelent, a belföldi rendelés pedig akkor ingyenes, ha az érték legalább 20 000 Ft vagy a vevő prémium. Ez négy szabály.

FeltételABCD
Külföldi címINNN
Érték >= 20 000-INN
Prémium tag--IN
AkcióKülföldi díjIngyenesIngyenesBelföldi díj

A belőle generált kód sorrendje egyezik a tábla oszlopsorrendjével, és minden ág egy oszlopra visszavezethető.

def shipping(order_total: int, premium: bool, foreign: bool) -> str:
    if foreign:                      # A
        return "foreign_fee"
    if order_total >= 20_000:        # B
        return "free"
    if premium:                      # C
        return "free"
    return "domestic_fee"            # D

Mikor érdemes. Három vagy több egymással kölcsönhatásban álló feltételnél, különösen jogosultságnál (szerep, tulajdonos-e, státusz, csomag), árazásnál, értesítési szabályoknál, állapotátmenetnél.

Tipikus hiba. A tábla kitöltése a teljes kombinációs tér helyett csak az eszünkbe jutó esetekkel. Pont a kimaradt kombináció a hiba. A másik gyakori hiba a nem független feltételek (például az összeg nagyobb 10 000-nél és az összeg nagyobb 20 000-nél egyszerre), ezek lehetetlen oszlopokat szülnek.

Mai alkalmazás. Ha egy kérés három vagy több feltételt kombinál, előbb a teljes döntési tábla készül el, a nem egyértelmű oszlopokat a megrendelővel tisztázzuk, és csak a jóváhagyott táblából generálunk kódot. A tábla oszlopaiból paraméterezett teszteset is lesz, egy oszlop egy teszt.


4. HIPO (IBM, 1970-es évek)

A HIPO (Hierarchy plus Input, Process, Output) az IBM tervezési és dokumentációs technikája volt, a HIPO. A Design Aid and Documentation Technique kézikönyv 1974-ben jelent meg. Három dokumentumtípusból áll.

  1. Tartalomjegyzék (VTOC, Visual Table of Contents). A rendszer funkcióinak fastruktúrája, számozott dobozokkal. A gyökér a teljes rendszer, a levelek a legkisebb önálló funkciók.
  2. Áttekintő diagram (overview diagram). Egy magas szintű funkció bemeneteit, feldolgozási lépéseit és kimeneteit mutatja három oszlopban, nyilakkal.
  3. Részletes diagram (detail diagram). Egy levélfunkció ugyanígy, de mezőszintű bemenetekkel és kimenetekkel, és hivatkozással a kódmodulra.

Elkészítés. A VTOC felülről lefelé készül, minden doboz igével kezdődő funkciónév (Számla kiállítása, Áfa kiszámítása). Minden levélhez IPO lap készül, ahol a bal oszlop az összes bemenet (forrással), a jobb oszlop az összes kimenet (céllal), a középső a lépések számozott listája. Ha egy kimenetet egyik lépés sem állít elő, vagy egy bemenetet egyik lépés sem használ, a terv hibás.

Mikor érdemes ma. Egy új funkció vagy API végpont tervezésénél. Az IPO lap közvetlenül átfordítható egy függvény szignatúrájára (bemenet), visszatérési típusára és mellékhatásaira (kimenet), valamint a törzs lépéseire.

Tipikus hiba. A mellékhatások (adatbázis írás, email küldés, log) kimaradnak a kimenet oszlopból, mert nem a visszatérési érték részei. Pedig ezek a kimenetek a legkockázatosabbak.


5. Dijkstra és a strukturált programozás

Az előzmény

Corrado Böhm és Giuseppe Jacopini 1966-ban bebizonyította, hogy bármely folyamatábra kifejezhető három szerkezettel (szekvencia, elágazás, ciklus), legfeljebb segédváltozók bevezetésével. Ez az elméleti alap.

Go To Statement Considered Harmful (1968)

Edsger W. Dijkstra 1968 márciusában a Communications of the ACM levelezési rovatában publikálta a rövid írást (a címet Niklaus Wirth szerkesztőként adta). Az érvelés lényege a következő. Egy program futását a program szövegéből kell tudnunk megérteni. Ha csak szekvencia, elágazás, ciklus és eljáráshívás van, akkor a futás bármely pillanata leírható néhány koordinátával (a szöveg pozíciója, valamint a ciklusok és hívások számlálói). A korlátlan goto ezt a koordinátarendszert lerombolja, ezért a program szövege és a futása közötti szakadék kezelhetetlenné válik.

Notes on Structured Programming (1969, könyvben 1972)

Az EWD249 jegyzet 1969-ben készült, és a Structured Programming kötetben (Dahl, Dijkstra, Hoare, 1972) jelent meg. Fő gondolatai lépésenként.

  1. A tesztelés csak a hiba jelenlétét tudja kimutatni, a hiányát soha. Ezért a helyességet a konstrukcióval kell megalapozni.
  2. A program felépítése absztrakciós rétegekben (a hírneves példa a prímszámok táblázatát kinyomtató program, amelyet egymás utáni finomítással fejt ki).
  3. Az absztrakció célja egy olyan új szemantikai szint létrehozása, ahol teljes pontossággal lehet gondolkodni.
  4. Felsorolás, elemzés és absztrakció mint a megértés három eszköze. A felsoroló érvelés (minden esetet megnézünk) csak kevés esetnél működik, a matematikai indukció a ciklusokhoz, az absztrakció a méret kezeléséhez kell.

The Humble Programmer (1972)

Dijkstra Turing díjas előadása 1972-ből. Kulcsgondolatai a mai gyakorlatra fordítva.

Guarded commands és a leggyengébb előfeltétel (1975, 1976)

Az 1975-ös CACM cikk (Guarded Commands, Nondeterminacy and Formal Derivation of Programs) és az 1976-os A Discipline of Programming könyv vezette be a leggyengébb előfeltétel (weakest precondition, wp) számítást.

Definíció. wp(S, R) azon kezdőállapotok halmaza (egy állítás), amelyekből indítva az S utasítás biztosan befejeződik, és a végén R igaz.

Szabályok.

wp(skip, R)         = R
wp(x := E, R)       = R, amelyben x helyére E kerül
wp(S1; S2, R)       = wp(S1, wp(S2, R))
wp(if B -> S1 [] not B -> S2 fi, R)
                    = (B => wp(S1, R)) and (not B => wp(S2, R))

Kézi példa. Mi kell ahhoz, hogy x := x + 1; y := y * 2 után y > x igaz legyen?

wp(y := y*2, y > x)       = 2y > x
wp(x := x+1, 2y > x)      = 2y > x + 1

Tehát a kezdőállapotban 2y > x + 1 kell. Ez visszafelé haladó gondolkodás. Az elvárt végállapotból indulunk, és utasításonként hátrafelé számoljuk ki, mit kell feltételezni az elején.

A guarded command (őrzött parancs) az if és do szerkezet nemdeterminisztikus változata. Az if B1 -> S1 [] B2 -> S2 fi szerkezetben bármelyik ág lefuthat, amelynek őre igaz, és ha egyik sem igaz, az hiba (abort). Ebből fakad egy tervezési fegyelem. Minden lehetséges esetnek kell őr, a hiányzó eset nem esik csendben egy else ágba.

Mai alkalmazás.

type Event = { kind: "paid" } | { kind: "refunded" } | { kind: "disputed" };

function handle(e: Event) {
  switch (e.kind) {
    case "paid": return markPaid();
    case "refunded": return markRefunded();
    case "disputed": return openDispute();
    default: {
      const _exhaustive: never = e;   // fordítási hiba, ha új eset jön
      throw new Error("Unhandled event");
    }
  }
}

Tipikus hiba. A strukturált programozást a goto tilalmára szűkítik. A lényeg a program szövegének és futásának megfeleltethetősége. Ugyanez sérül mély callback láncnál, rejtett eseménykezelőnél, globális mutálható állapotnál, kivétellel vezérelt folyamatnál.


6. Floyd, Hoare és a ciklusinvariáns

Floyd (1967)

Robert W. Floyd Assigning Meanings to Programs (1967) cikke a folyamatábra éleihez állításokat rendelt, és megmutatta, hogyan bizonyítható, hogy ha egy él állítása igaz, akkor a következő élé is az. Két dolgot kell bizonyítani. A részleges helyességet (ha a program megáll, jó eredményt ad) és a befejeződést (meg is áll). A befejeződéshez Floyd egy jól rendezett halmazba képező mértéket (ma variáns vagy terminációs függvény) javasolt, amely minden ciklusmenetben szigorúan csökken.

Hoare (1969)

C. A. R. Hoare An Axiomatic Basis for Computer Programming (CACM, 1969) cikke a folyamatábra nélküli, szöveges formát adta. A {P} S {Q} Hoare hármas azt állítja, hogy ha P igaz S előtt, és S befejeződik, akkor Q igaz utána. A legfontosabb szabályok.

Értékadás:      {Q[x := E]}  x := E  {Q}
Szekvencia:     {P} S1 {R},  {R} S2 {Q}   =>  {P} S1; S2 {Q}
Elágazás:       {P and B} S1 {Q}, {P and not B} S2 {Q}  =>  {P} if B then S1 else S2 {Q}
Ciklus:         {I and B} S {I}   =>  {I} while B do S {I and not B}
Következmény:   P => P',  {P'} S {Q'},  Q' => Q   =>  {P} S {Q}

A ciklusinvariáns gyakorlati használata

A ciklusinvariáns olyan állítás, amely a ciklus előtt igaz, minden menet elején és végén igaz, és a kilépési feltétellel együtt kiadja a kívánt eredményt. Ez a korszak talán legjobban átvihető gyakorlati eszköze.

Lépések egy ciklus megírásához (David Gries The Science of Programming, 1981 alapján, amely Dijkstra módszerét tankönyvvé tette).

  1. Utófeltétel megfogalmazása. Mit akarunk a ciklus után? Például s az a[0..n) elemek összege.
  2. Invariáns levezetése az utófeltételből, általában egy konstans változóvá tételével. s az a[0..i) elemek összege, és 0 <= i <= n.
  3. Inicializálás, amely az invariánst igazzá teszi. i = 0, s = 0 (üres összeg).
  4. Kilépési feltétel, amely az invariánssal együtt kiadja az utófeltételt. i == n, tehát a ciklusfeltétel i != n vagy i < n.
  5. Haladás, amely csökkenti a variánst (n - i).
  6. Invariáns megőrzése a haladás mellett. Az i növelése előtt s += a[i].
def total(a):
    s, i = 0, 0
    # INV: s == sum(a[0:i]) and 0 <= i <= len(a)
    # VARIANS: len(a) - i, minden menetben csökken, alulról korlátos
    while i < len(a):
        s += a[i]
        i += 1
        assert s == sum(a[:i])      # csak fejlesztés alatt, debug módban
    # INV and not (i < len(a))  =>  i == len(a)  =>  s == sum(a)
    return s

A bináris keresés invariánsa a következő. Ha x benne van a-ban, akkor a[lo..hi] tartományban van. A korábbi nyomkövető táblás hibát (lo < hi) az invariáns azonnal mutatja. Ha lo == hi, a tartomány egy elemű és nem üres, a ciklusnak tehát még futnia kell.

Mikor érdemes. Minden nem triviális ciklusnál (ahol az index nem egyszerűen végigmegy egy listán), kétmutatós algoritmusnál, csúszóablaknál, lapozásnál kurzorral, újrapróbálási ciklusnál, összefésülésnél.

Tipikus hiba. Az invariánst utólag, a kész kódhoz próbálják kitalálni. Akkor csak a kód leírása lesz, nem az ellenőrzése. A másik hiba a variáns elhagyása. A végtelen ciklus nem a részleges helyesség, hanem a befejeződés hibája.

Mai alkalmazás. A kritikus ciklus fölé kommentben kerül az invariáns és a variáns. Tesztben a tulajdonság alapú tesztelés (Hypothesis, fast-check) az invariánst és az utófeltételt véletlen bemenetekre ellenőrzi. Ez a Hoare hármas mai, gépesített formája.


7. Wirth. Lépésenkénti finomítás

Program Development by Stepwise Refinement (1971)

Niklaus Wirth 1971 áprilisában a CACM-ben publikálta a cikket, amely a nyolc királynő problémán mutatja be a módszert. A program egy durva, természetes nyelvű megfogalmazással indul, majd lépésenként minden utasítást részletesebb utasításokra bontunk, és ezzel párhuzamosan finomítjuk az adatok ábrázolását is.

Lépések.

  1. A teljes feladat egyetlen absztrakt utasításként. helyezz el nyolc királynőt úgy, hogy egyik se üsse a másikat.
  2. Felbontás néhány lépésre, amelyek még mindig absztraktak. vedd sorra az oszlopokat; minden oszlopban próbálj ki egy biztonságos sort; ha nincs, lépj vissza.
  3. Az adatok absztrakt megnevezése. melyik sor szabad, melyik átló szabad.
  4. Minden absztrakt utasítás és adat további finomítása, amíg a programnyelv utasításaihoz nem érünk.
  5. Minden finomítási lépés után döntés arról, melyik alternatívát választjuk, és rögzítése, miért.

Wirth két szabályt emelt ki. A finomítás során a döntéseket a lehető legkésőbb kell meghozni, és minden lépés egy mérlegelés eredménye (hatékonyság, tárigény, érthetőség). A második, hogy az adatszerkezet finomítása és az algoritmus finomítása kéz a kézben jár.

Algorithms + Data Structures = Programs (1976)

A könyv címe maga a tétel. Egy program az algoritmus és az adatszerkezet együttes terméke, és egyik sem tervezhető a másik nélkül. Wirth Pascalban mutatja be az alapvető adatszerkezeteket (tömb, rekord, halmaz, szekvenciális fájl, lista, fa), a rendezést, a rekurziót és a visszalépéses keresést, egy egyszerű fordító felépítésével zárva.

Mai alkalmazás.

Tipikus hiba. A finomítás közben a felsőbb szint feltételezései elvesznek. Ezért minden finomított részhez kommentben ott marad az az absztrakt utasítás, amit kifejt.


8. Parnas. Modulok, változás és a racionális folyamat színlelése

Információrejtés (1972)

David L. Parnas On the Criteria To Be Used in Decomposing Systems into Modules (CACM, 1972. december) cikke a KWIC (Key Word In Context) indexelő programot két módon bontja modulokra. Az első bontás a feldolgozás lépései szerint halad (bemenet, körkörös eltolás, rendezés, kimenet), ahogy egy folyamatábrából adódik. A második a tervezési döntések szerint, ahol minden modul egy olyan döntést rejt el, amely valószínűleg változik (például a sorok tárolásának módja). A második bontásban a tárolás megváltoztatása egyetlen modult érint, az elsőben mindet.

Az eljárás.

  1. A változó döntések listája. Mi változhat a rendszer élete során? Adatformátum, tárolási mód, külső szolgáltató, algoritmus, hardver, felhasználói felület, üzleti szabály.
  2. Minden döntéshez egy modul, amely azt titokként rejti.
  3. A modul felülete csak olyan feltételezéseket tartalmaz, amelyek a titok megváltozásakor is igazak maradnak.
  4. Ellenőrzés. Minden várható változáshoz megnézzük, hány modult érint. A jó bontásnál egyet.

Uses hierarchia és a bővíthetőség (1979)

A Designing Software for Ease of Extension and Contraction (IEEE Transactions on Software Engineering, 1979) cikk a programcsaládokról szól, vagyis arról, hogy egy rendszerből könnyen lehessen kisebb (részhalmaz) és nagyobb (kiterjesztés) változatot készíteni.

Uses reláció. A modul A használja B-t, ha A helyes működéséhez B helyes működése kell (nem azonos azzal, hogy A meghívja B-t). A használati reláció legyen körmentes, így szintekbe rendezhető. Minden szint egy működő, tesztelhető virtuális gép, és a rendszer bármely alsó szintig lecsupaszítható.

Mikor engedjük meg, hogy A használja B-t (Parnas kritériumai összefoglalva).

Mai alkalmazás. A modulok közötti import gráf legyen körmentes, ezt eszköz ellenőrzi (madge --circular, import-linter, dependency-cruiser). Egy új funkciót úgy tervezünk, hogy a minimális változat (a legkisebb hasznos részhalmaz) előbb működjön.

A-7E és a táblázatos specifikáció

Az amerikai haditengerészet kutatólaboratóriumában (NRL) futott Software Cost Reduction projekt az A-7E repülőgép fedélzeti szoftverét tervezte újra. Kathryn Heninger 1980-as cikke (Specifying Software Requirements for Complex Systems) írja le a követelménydokumentumot. Jellemzői.

A racionális tervezési folyamat színlelése (1986)

Parnas és Paul Clements A Rational Design Process. How and Why to Fake It (IEEE TSE, 1986) cikke elismeri, hogy senki nem tervez lineárisan, követelményektől a kódig. A dokumentációt mégis úgy kell megírni, mintha így történt volna, mert az olvasónak a végső, rendezett indoklás kell, nem a tervezés kanyargós története.

A dokumentumok sorrendje a cikk szerint.

  1. Követelménydokumentum.
  2. Modulkalauz (melyik modul milyen titkot rejt).
  3. Modulfelület specifikációk.
  4. Használati hierarchia (uses).
  5. Modulon belüli terv.
  6. Kód.
  7. Tesztelés és karbantartás.

A dokumentációban minden döntésnél ott áll az elvetett alternatíva és az elvetés oka. A téves utakat nem a történet részeként, hanem a döntések indoklásaként rögzítjük.

Mai alkalmazás. Ez az ADR (Architecture Decision Record) gyakorlat őse. Egy AI asszisztált munkamenet kaotikus. Próbálkozás, visszalépés, újragenerálás. A munkamenet végén a végső állapotot írjuk le rendezetten (mit, miért, mit vetettünk el), a promptok történetét nem.

Tipikus hiba. A modulokat a fájlrendszer vagy a keretrendszer konvenciója szerint vágják (controllers, services, utils), a titkok szerint soha. Így egy szolgáltatócsere tíz fájlt érint.


9. Yourdon és Constantine. Structured Design

Háttér

Larry Constantine az 1960-as évek második felében dolgozta ki a csatolás és a kohézió fogalmát. Az első publikáció W. Stevens, G. Myers és L. Constantine Structured Design cikke volt (IBM Systems Journal, 1974). A teljes módszert Edward Yourdon és Constantine könyve, a Structured Design. Fundamentals of a Discipline of Computer Program and Systems Design írja le (a Yourdon Press kiadás 1975 és 1978 között, a Prentice Hall kiadás 1979). Glenford Myers Composite/Structured Design (1978) könyve ugyanezt a fogalomrendszert fejtette ki.

Csatolás (coupling)

A csatolás két modul közötti függés erőssége, és a lehető leggyengébbre törekszünk. Skála a legrosszabbtól a legjobbig.

FokozatMit jelentPéldaMai megfelelő
Tartalmi (content)Egyik modul belenyúl a másik belsejébe, módosítja a kódját vagy lokális adatát, belső címkére ugrikAssemblyben ugrás egy másik rutin közepéreMonkey patching, privát mező elérése reflexióval, másik modul belső tábláinak közvetlen írása
Közös (common)Több modul ugyanazt a globális adatterületet írja és olvassaFORTRAN COMMON blokkGlobális mutálható állapot, közös singleton, több szolgáltatás ugyanabba a táblába ír
Külső (external)Modulok egy kívülről előírt formátumon, protokollon vagy eszközön osztoznakKözös fájlformátum, eszközinterfészKözös környezeti változók, közös fájl vagy üzenetformátum szerződés nélkül
Vezérlési (control)Egyik modul jelzőt ad át, amely a másik belső logikáját irányítjaprocess(data, mode=3)Boolean paraméter, amely két teljesen eltérő viselkedést kapcsol (save(x, isDraft))
Bélyeg (stamp)Egy összetett adatszerkezet egészét adják át, de csak egy részét használjákTeljes rekord átadása egy mező kedvéértTeljes user objektum átadása, amikor csak az id kell
Adat (data)Csak az elemi, szükséges adatok mennek át paraméterkénttax(amount, rate)Tiszta függvény minimális paraméterekkel

Egyes szerzők a legjobb végére egy hetedik fokozatot is tesznek (nincs csatolás, vagy üzenetcsatolás), ez a független vagy csak üzenetekkel kommunikáló modul.

Mérés a gyakorlatban. Két modul között a legrosszabb előforduló kapcsolatfajta számít. Ha egy modul tíz adatparaméterrel és egy globális változóval kommunikál, a csatolás közös.

Kohézió (cohesion)

A kohézió azt méri, mennyire tartoznak össze egy modul elemei, és itt a lehető legerősebb a cél. Skála a legrosszabbtól a legjobbig.

FokozatMi tartja összeFelismerés
Esetleges (coincidental)Semmi, véletlenül kerültek egy helyreutils.py, helpers.ts, misc
Logikai (logical)Ugyanabba a kategóriába esnek, egy jelző választ köztükhandleAll(type) egy nagy switch-csel, amely olvas, ír, töröl
Időbeli (temporal)Ugyanakkor kell lefutniukinit(), amely naplót nyit, cache-t ürít, számlálót nulláz
Eljárási (procedural)Egy vezérlési sorrend lépései, de más adaton dolgoznakdoStep1ThenStep2(), ahol a lépések adatban nem függnek össze
Kommunikációs (communicational)Ugyanazon az adaton dolgoznakEgy rekordból riportot és összesítést is készít
Szekvenciális (sequential)Egyik kimenete a másik bemeneteBeolvas, tisztít, validál, egy adatfolyamként
Funkcionális (functional)Minden elem egyetlen, jól megnevezhető funkciót szolgálcalculateVat(net, rate)

A mondatpróba (Yourdon és Constantine nyomán). A modul feladatát egyetlen mondatban kell leírni, és a mondat alakja elárulja a kohéziót.

Fan-in, fan-out és a structure chart

Structure chart. A modulok hívási hierarchiáját mutatja téglalapokkal és nyilakkal. Kis körös nyilak jelölik a hívási vonalak mentén az átadott adatot (üres kör) és vezérlőjelzőt (teli kör), a könyvtári modulokat pedig függőleges belső vonalak.

Fan-out egy modul által közvetlenül hívott modulok száma. Yourdon és Constantine ökölszabálya szerint ha ez hét fölött van, a modul valószínűleg túl sokat koordinál, közbülső szintet kell beiktatni. A kettő alatti fan-out egy hosszú lánc jele, amely talán összevonható.

Fan-in azoknak a moduloknak a száma, amelyek egy adott modult hívnak. A magas fan-in jó, mert újrahasznosítást jelez, feltéve hogy a modul funkcionális kohéziójú. Logikai kohéziójú modulnál a magas fan-in veszélyes, mert mindenki más részét használja.

Az alakzat. A jó szerkezet mecset alakú. Felül néhány koordináló modul alacsony fan-innel, középen a fan-out kiszélesedik, alul a közös, magas fan-inű segédmodulok.

Transzformációs és tranzakciós elemzés

Két eljárás az adatfolyam diagram (DFD, DeMarco módszerének központi eleme) structure charttá alakítására.

Transzformációs elemzés (transform analysis) lépései.

  1. A DFD-n megkeressük a bemeneti ágat (afferens), ahol a nyers adat egyre absztraktabbá válik.
  2. Megkeressük a kimeneti ágat (efferens), ahol az absztrakt eredmény egyre konkrétabb kimenetté alakul.
  3. A kettő között a központi transzformáció, a legabsztraktabb bemenet és kimenet közötti rész.
  4. A structure chart tetején egy főmodul, alatta három ág, a bemenet beszerzése, a központi transzformáció, a kimenet előállítása.

Tranzakciós elemzés (transaction analysis) akkor kell, ha a bemenet típusa szerint teljesen eltérő feldolgozás következik. Egy elosztó modul a tranzakció típusa szerint külön modulokat hív, mindegyik egy tranzakciótípust kezel. Ez a mai router és handler minta.

Mai alkalmazás. Generált kód átnézésekor a csatolás és a kohézió skálája a mérce. Egy vezérlő boolean paraméterből két függvény lesz, egy utils fájl függvényei a felhasználási helyükhöz költöznek vagy funkcionális modulokba kerülnek, és a teljes objektum helyett a szükséges mezők mennek át, ahol ez nem rontja az olvashatóságot. Hétnél több dolgot hívó függvénynél közbülső lépések kerülnek be.

Tipikus hiba. A bélyegcsatolás buzgó kiirtása túl hosszú paraméterlistákhoz vezet. A mérlegelés lényege az, hogy a modul a szükséges adatot kapja, és a felülete stabil maradjon.


10. DeMarco. Structured Analysis

Tom DeMarco Structured Analysis and System Specification (Yourdon Press, 1978, a Prentice Hall kiadás 1979) könyve a strukturált elemzés legolvasottabb leírása. Chris Gane és Trish Sarson ugyanebben az időben (1979) egy rokon jelölést publikált. A módszer célja a követelmény specifikáció, amely grafikus, felülről lefelé tagolt, minimálisan redundáns és karbantartható.

A specifikáció három eleme.

Adatfolyam diagram (DFD)

Elemek (DeMarco és Yourdon jelölés).

ElemJelJelentés
Folyamat (process, bubble)körAdatot átalakító tevékenység, igés névvel és számmal
Adatfolyamnyíl, feliratozvaMozgásban lévő adat, főnévvel
Adattárkét párhuzamos vonalNyugvó adat
Terminátor (külső entitás)téglalapA rendszeren kívüli forrás vagy nyelő

Szintezés (leveling). A legfelső a kontextusdiagram, egyetlen folyamattal (maga a rendszer) és az összes külső entitással. Ezt bontja szét a 0. szintű diagram 5 és 9 közötti folyamatra, mindegyiket tovább egy alsóbb szintű diagram, a számozás hierarchikus (3, 3.1, 3.1.2). A bontás addig tart, amíg egy folyamat egy oldalnyi mini specifikációval leírható.

Kiegyensúlyozás (balancing). Egy folyamat alsóbb szintű kifejtésének be és kimenő adatfolyamai hiánytalanul megegyeznek a szülő folyamat be és kimenő folyamaival. Ez a DFD legfontosabb ellenőrzése.

Szabályok, amelyek hibát jeleznek.

Adatszótár (data dictionary)

Minden adatfolyam, adattár és elemi adat definíciója, saját jelöléssel.

JelJelentés
=áll belőle
+és
[ a | b ]választás, egyik a felsoroltak közül
{ x }ismétlés (iteráció), alsó és felső határral 1{x}10
( x )opcionális
* ... *megjegyzés
@kulcsmező (egyes változatokban)

Példa.

rendelés         = @rendelés_azonosító + vevő_azonosító + rendelés_dátuma
                   + 1{rendelési_tétel}50 + (kupon_kód) + szállítási_cím
rendelési_tétel  = termék_azonosító + mennyiség + egységár
szállítási_cím   = irányítószám + település + közterület + (emelet_ajtó)
mennyiség        = * egész szám, 1 és 999 között *
fizetési_mód     = [ "kártya" | "átutalás" | "utánvét" ]

Mini specifikáció (mini spec)

Minden legalsó szintű folyamathoz egy rövid leírás, amely az adatszótárban definiált nevekkel dolgozik. Három formája van.

  1. Strukturált angol (structured English, magyarul strukturált magyar). Korlátozott szókincs (igék, adatszótári nevek, néhány kulcsszó), a három alapszerkezettel. Melléknév, határozatlan kifejezés (néhány, általában, megfelelő) tilos.
  2. Döntési tábla a sok feltételt kombináló logikára.
  3. Döntési fa a kevés feltételt, de hierarchikusan elágazó logikára.
FOLYAMAT 3.2  Szállítási díj meghatározása
HA szállítási_cím.ország nem Magyarország
    AKKOR szállítási_díj = külföldi_díj
KÜLÖNBEN
    HA rendelés_értéke >= 20000 VAGY vevő.prémium
        AKKOR szállítási_díj = 0
    KÜLÖNBEN szállítási_díj = belföldi_díj

Mai alkalmazás.

Tipikus hiba. A DFD-be vezérlési logikát rajzolnak, vagy a fizikai megvalósítást (képernyők, táblák neve) tükrözi a logikai adatfolyam helyett. A másik a kiegyensúlyozás elhagyása, ami miatt az alsóbb szinteken új, sehonnan nem érkező adat jelenik meg.


11. Jackson Structured Programming és Jackson System Development

JSP (1975)

Michael A. Jackson Principles of Program Design (1975) könyve egy alapvetően eltérő elvet vezetett be. A program szerkezetét az adatok szerkezetéből kell levezetni. Ha egy program egy bemeneti adatfolyamot dolgoz fel és egy kimenetit állít elő, a program szerkezete a két adatszerkezet összefésülése.

Jelölés. Fastruktúra, három elemtípussal.

Lépések.

  1. A bemeneti adatszerkezet felrajzolása fastruktúraként.
  2. A kimeneti adatszerkezet felrajzolása ugyanígy.
  3. A megfeleltetések megkeresése. Mely bemeneti és kimeneti elemek felelnek meg egymásnak (azonos számban és sorrendben fordulnak elő).
  4. A program szerkezetének összeállítása a megfeleltetett elemekből.
  5. Az elemi műveletek listája (olvasás, írás, számítás) és hozzárendelésük a program szerkezetének elemeihez.
  6. A feltételek (iterációk és szelekciók feltételei) megfogalmazása.
  7. A szerkezet átírása szöveges formába (schematic logic), majd kódba.

Példa. Bemenet egy tranzakciós fájl, ügyfél szerint rendezve, egy ügyfélnek több tétele lehet. Kimenet egy riport, ügyfelenként egy összesítő sorral.

Bemenet:  FÁJL = { ÜGYFÉLCSOPORT }*        ÜGYFÉLCSOPORT = { TÉTEL }*
Kimenet:  RIPORT = FEJLÉC + { ÖSSZESÍTŐ_SOR }* + VÉGÖSSZEG

Program:
  RIPORT_KÉSZÍTÉS
    fejléc írása; első rekord olvasása
    ÜGYFÉLCSOPORT_FELDOLGOZÁS * (amíg nincs fájlvége)
      ügyfélösszeg := 0; aktuális_ügyfél := rekord.ügyfél
      TÉTEL_FELDOLGOZÁS * (amíg nincs fájlvége és rekord.ügyfél = aktuális_ügyfél)
        ügyfélösszeg += rekord.összeg; következő rekord olvasása
      összesítő sor írása; végösszeg += ügyfélösszeg
    végösszeg írása

Az előreolvasás (read ahead) elve, vagyis egy rekord előre beolvasása, hogy a ciklusfeltétel kiértékelhető legyen, a JSP alapszabálya.

Szerkezeti ütközések (structure clash). Ha a bemenet és a kimenet szerkezete nem feleltethető meg, három fajta ütközés lehet.

Programinverzió (program inversion). A két program közötti közbülső fájl kiküszöbölhető, ha az egyik programot alprogrammá fordítjuk, amely minden hívásra egy rekordot dolgoz fel, és megjegyzi, hol tartott. Ez a korutin és a generátor őse.

Visszalépés (backtracking). Ha egy szelekció feltétele csak a feldolgozás közben derül ki, a programot úgy írjuk, hogy feltételezi az egyik ágat (posit), és ha kiderül, hogy téves, visszalép (quit), majd a másik ágat követi (admit).

JSD (1983)

A System Development (1983) könyv a JSP elveit rendszerszintre emelte. A JSD lépései.

  1. Entitás és akció lépés. A valós világ entitásainak és az őket érintő eseményeknek (akcióknak) a listája.
  2. Entitásstruktúra lépés. Minden entitás életciklusa JSP fastruktúraként (egy könyv beszerezve, kölcsönözve és visszahozva sokszor, majd selejtezve).
  3. Kezdeti modell lépés. Minden entitás egy szekvenciális folyamat a modellben, amely a valós világ eseményeit kapja.
  4. Funkció lépés. A kimeneteket előállító funkciók hozzáadása a modellhez.
  5. Rendszeridőzítés lépés. A folyamatok közötti időzítési követelmények.
  6. Megvalósítási lépés. A sok folyamat leképezése kevés processzorra, programinverzióval és állapotvektorokkal.

Mai alkalmazás.

Tipikus hiba. A program szerkezetét a folyamat lépései szerint írják meg (beolvas mindent, feldolgoz, kiír), és a csoporthatárokat jelzőváltozókkal toldozzák. A JSP diagnózisa ilyenkor egy fel nem ismert szerkezeti ütközés.


12. Fagan inspekció (1976)

Michael E. Fagan Design and Code Inspections to Reduce Errors in Program Development (IBM Systems Journal, 1976) cikke az IBM-nél kidolgozott formális átnézési folyamatot írja le. A cikkben közölt IBM adatok szerint az inspekció a hibák nagy részét a tesztelés előtt kiszűrte, és a teljes fejlesztési ráfordításon nettó megtakarítást hozott. Fagan 1986-ban egy második cikkben (Advances in Software Inspections, IEEE TSE) frissítette a módszert.

Szerepek

SzerepFeladat
ModerátorVezeti a folyamatot, ő a kulcsszereplő. Képzett, a szerzőtől független, felel az ütemezésért, a jegyzőkönyvért és a követésért
Tervező (designer)Az inspektált terv szerzője
Kódoló vagy megvalósító (coder, implementor)A kód szerzője
TesztelőA tesztelés szempontját képviseli
Felolvasó (reader)Az ülésen saját szavaival elmondja, mit csinál a kód. A szerző nem lehet felolvasó
Jegyző (recorder)Rögzíti a talált hibákat, gyakran a moderátor

Az ideális létszám négy fő.

Fázisok

  1. Tervezés (planning). A moderátor ellenőrzi, hogy az anyag eléri-e a belépési feltételeket (lefordul, a terv megvan), összeállítja a csapatot, kiosztja az anyagot.
  2. Áttekintés (overview). A szerző röviden bemutatja a kontextust a csapatnak. Kódinspekciónál gyakran elmarad.
  3. Felkészülés (preparation). Mindenki egyénileg tanulmányozza az anyagot a checklisttel. Ebben a fázisban még nem a hibakeresés a cél, hanem a megértés, a hibák nagy része mégis itt jelenik meg.
  4. Inspekciós ülés (inspection meeting). A felolvasó végigmegy az anyagon, a résztvevők jelzik a hibákat. Csak hibát rögzítenek, megoldást nem keresnek. Az ülés legfeljebb két óra, naponta legfeljebb kettő.
  5. Javítás (rework). A szerző kijavít minden rögzített hibát.
  6. Követés (follow up). A moderátor ellenőrzi a javítást. Ha az anyag jelentős része (Fagan eredeti küszöbe 5 százalék) megváltozott, újabb inspekció jön.

Sebesség és mérőszámok

Fagan a felkészülésre és az ülésre is óránkénti sebességkorlátot adott. Kódinspekciónál nagyságrendileg száz körüli forrássort óránként, és ennél jóval lassabbat bonyolult kódnál. A gyorsabb haladás bizonyítottan kevesebb hibát talál.

Mért mennyiségek.

Ezek a számok nem a fejlesztők értékelésére valók, hanem a folyamat javítására. Fagan kifejezetten ellenezte, hogy a hibaszám teljesítményértékelés alapja legyen.

Checklist

A checklist az adott nyelv és szervezet korábbi hibáiból épül, és folyamatosan frissül. Egy kódinspekciós checklist tipikus kérdései.

Mai alkalmazás. A Fagan inspekció szerepei egyetlen ágensen belül is eljátszhatók, de csak külön menetekben. A generálás után külön önátnézési kör következik, amelyben a felolvasó (a fejlesztő, vagy egy új menetben maga a modell) saját szavaival, soronként elmondja, mit csinál a kód. A parafrázis kényszere felszínre hozza az eltérést a szándék és a kód között. Utána checklist jön, amelyet érdemes az AI generált kód tipikus hibáihoz igazítani. Az eredmény egy hibalista, és a javítás csak utána következik.

Tipikus hiba. Az inspekció beszélgetéssé válik, a résztvevők megoldásokat vitatnak, és két óra alatt fele annyi anyag fogy. A másik hiba a felkészülés kihagyása, ilyenkor az ülés felolvasássá silányul.


13. Mills, Baker, Weinberg. Csapat, cleanroom, egoless programming

Chief programmer team (Mills, Baker)

Harlan Mills az 1960-as évek végén javasolta, hogy egy programozócsapat ne egyenrangú tagokból álljon, hanem egy sebészcsapat mintájára épüljön fel. F. Terry Baker 1972-ben számolt be a módszer első nagy alkalmazásáról (Chief Programmer Team Management of Production Programming, IBM Systems Journal), a New York Times archívumrendszerének fejlesztéséről, amely kiemelkedően alacsony hibaszámmal és termelékenységgel zárult.

Szerepek (Baker és Brooks leírása alapján).

SzerepFeladat
Chief programmer (főprogramozó)Megtervezi a rendszert, megírja a kritikus kódot, átnézi a többit
Backup programmer (helyettes)Mindent tud, amit a főprogramozó, át tudja venni, alternatívákat dolgoz ki
Program librarian (könyvtáros)Kezeli a programkönyvtárat (forrás, futási eredmények, verziók), ő adja le a futtatásokat
AdministratorPénz, ember, hely, gép
ToolsmithSpeciális eszközöket ír a főprogramozónak
Language lawyerA nyelv és a fordító mélységeinek szakértője
TesterTeszteseteket ír és futtat

A Development Support Library (a programkönyvtár fizikai és gépi formája) a mai verziókezelő őse. Minden kód nyilvános, minden futtatás eredménye archivált.

Weinberg. The Psychology of Computer Programming (1971)

Gerald M. Weinberg könyve vezette be az egoless programming fogalmát. A programozó nem azonosítja magát a kódjával, ezért nem védekezik, amikor mások hibát találnak benne, és maga kéri a kollégák átnézését. A program a csapaté. Weinberg megfigyelése szerint a programozó saját hibáit nehezebben látja, mint másokéit, mert a szeme azt olvassa, amit írni akart.

Mai alkalmazás. Az AI által generált kódra különösen igaz, hogy az író (a modell) a saját szándékát látja benne. Ezért a generálás és az átnézés külön lépés, és az átnézés célja a hiba megtalálása, nem a kód védelme. Egy jelzett hibát nem kimagyarázni kell, hanem reprodukálni és javítani.

Cleanroom (Mills, 1980-as évek)

Mills, Michael Dyer és Richard Linger 1987-ben írta le a cleanroom szoftverfejlesztést (Cleanroom Software Engineering, IEEE Software). A név a félvezetőgyártás tiszta szobájára utal. A hibát nem utólag szűrjük ki, hanem nem engedjük be.

Alapelvek.

  1. Inkrementális fejlesztés. A rendszer kis, egymásra épülő növekményekben készül, mindegyik végig van vive a teljes folyamaton.
  2. Box structures specifikáció és tervezés. Minden komponens három nézetben készül. A fekete doboz (black box) a külső viselkedés, az ingerek története alapján adott válasz. Az állapotdoboz (state box) a történetet állapottal váltja ki, vagyis válasz = f(inger, állapot). Végül az átlátszó doboz (clear box) a megvalósítás strukturált vezérléssel, amely más fekete dobozokat használhat.
  3. Helyességellenőrzés csapatban (functional verification). A fejlesztők nem futtatják a kódjukat. A helyességet a csapat ülésen, a strukturált program minden szerkezetére feltett helyességi kérdésekkel igazolja (a függvény szemantikán alapuló, kevésbé formális Hoare logika).
  4. Statisztikai használati tesztelés. Külön tesztcsapat a valószínű használat profilja (usage model, Markov lánc) szerint generál teszteseteket, és a mért hibaközi idő (MTTF) alapján minősíti a szoftvert.

Mai alkalmazás. A doboznézetek hasznosak egy API vagy komponens specifikálásánál. Előbb a fekete doboz (milyen kérésre milyen válasz), utána az állapotdoboz (milyen állapotot tart fenn, hol), végül a kód. A statisztikai használati tesztelés gondolata a mai valós forgalom alapú teszteléshez és a canary kiadáshoz vezet. A tesztelés a valószínű használatot fedje le súly szerint, ne csak a fejlesztő kedvenc eseteit.


14. Brooks. The Mythical Man-Month (1975) és No Silver Bullet (1986)

Frederick P. Brooks Jr. az IBM System/360 és OS/360 fejlesztését vezette az 1960-as években. The Mythical Man-Month (1975) ennek tapasztalataiból született esszégyűjtemény. A 20. évfordulós kiadás (1995) a No Silver Bullet esszét és egy visszatekintő fejezetet is tartalmaz.

A fő tételek

A kátránygödör. A programozási rendszertermék (programming systems product) nagyjából kilencszer annyi munka, mint egy önálló program. Háromszoros szorzó jön abból, hogy termék legyen (általánosítva, tesztelve, dokumentálva, karbantartható), és újabb háromszoros abból, hogy rendszer része legyen (interfészek, integráció).

A mitikus emberhónap. Az ember és a hónap csak akkor cserélhető fel, ha a feladat kommunikáció nélkül felosztható. A szoftverfejlesztésben a betanítás és az egymás közötti kommunikáció költsége (n fő között n(n-1)/2 csatorna) gyorsan felemészti a hozzáadott munkaerőt. Ütemezési hüvelykujjszabály Brookstól. Egyharmad tervezés, egyhatod kódolás, egynegyed komponensteszt és korai rendszerteszt, egynegyed teljes rendszerteszt.

Brooks törvénye. Egy késésben lévő szoftverprojekthez embert adni csak tovább késlelteti.

A sebészcsapat. A Mills féle chief programmer team csapatszerkezete. Kevés kiváló ember jobb, mint sok átlagos, mert a termelékenységbeli különbség egy nagyságrend is lehet.

Arisztokrácia, demokrácia és rendszertervezés. A koncepcionális integritás (conceptual integrity) a legfontosabb szempont. Egy rendszer egy következetes gondolatot tükrözzön, ezért a tervezést egy vagy nagyon kevés elmének (architektnek) kell irányítania. A megvalósítás ettől még lehet demokratikus.

A második rendszer hatás. A tervező második rendszere a legveszélyesebb, mert minden belekerül, amit az elsőből kihagyott.

Továbbadni az igét. A specifikációt egyértelművé kell tenni (formális és prózai leírás együtt), és egyetlen implementáció legyen a definíció.

Miért omlott össze Bábel tornya. A kudarc a kommunikáció és a szervezés hiánya. Projektkönyv (project workbook) kell, amelyben minden dokumentum mindenkinek elérhető.

Számolj előre. A termelékenység a rendszer bonyolultságával erősen csökken. A becslés a teljes munkára vonatkozzon, ne csak a kódolásra.

Egy font a kétfontos zsákban. A méret erőforrás, költségvetése kell legyen. A memóriakorlát kezelésében a reprezentáció (adatszerkezet) a lényeg. Brooks megfogalmazásában a táblázatokból a folyamatábra kitalálható, fordítva viszont nem.

A dokumentációs hipotézis. Néhány kulcsdokumentum (célok, specifikáció, ütemterv, költségvetés, szervezeti ábra, helyiségek) köré szerveződik a vezetői munka. Ezek megírása gondolkodásra kényszerít.

Tervezz eldobásra. Az első rendszer eldobásra kerül, akár tervezzük, akár nem, ezért tervezzük meg. (Az 1995-ös kiadásban Brooks ezt a vízesés feltételezéseként bírálta, és az inkrementális fejlesztést tartotta jobbnak.)

Szerszámok. Közös, karbantartott eszközök, interaktív programozás, magas szintű nyelv.

Az egész és a részek. Fentről lefelé tervezés, a rendszer integrálása lépésenként, egyszerre csak egy komponens hozzáadásával.

Egy évet késni napról napra. A katasztrófát nem a nagy csapások, hanem a sok apró csúszás okozza. Konkrét, ellenőrizhető mérföldkövek kellenek (éles határú, nem a kódolás 90 százalékban kész fajtájú).

A másik arc. A program két közönségnek íródik, a gépnek és az embernek. A dokumentáció a forrásban éljen (önmagát dokumentáló program).

No Silver Bullet (1986)

Az esszé 1986-ban IFIP konferencián hangzott el, 1987-ben az IEEE Computerben jelent meg. Tétele szerint egyetlen technológiai vagy módszertani fejlesztés sem hoz egy évtizeden belül egy nagyságrendnyi javulást a termelékenységben, a megbízhatóságban vagy az egyszerűségben.

Lényegi és járulékos bonyolultság. A járulékos bonyolultság (accidental) a megvalósítás nehézségeiből fakad (gépi kód, fordulási idő, nyelvi korlátok), ezt a magas szintű nyelvek, az időosztás és az egységes környezetek nagyrészt leszedték. A lényegi bonyolultság (essential) maga a fogalmi konstrukció, az adathalmazok, kapcsolatok, algoritmusok és függvényhívások összefonódott rendszere. Négy tulajdonsága van.

  1. Bonyolultság (complexity). Nincs két egyforma rész, az állapottér robbanásszerű.
  2. Megfelelés (conformity). A szoftvernek önkényes emberi intézményekhez és rendszerekhez kell igazodnia.
  3. Változékonyság (changeability). A szoftvert folyamatosan módosítják, mert módosítható.
  4. Láthatatlanság (invisibility). A szoftvernek nincs természetes térbeli ábrázolása.

Ígéretes támadások a lényeg ellen. Kész szoftver vásárlása építés helyett. Gyors prototípus a követelmények finomítására. Inkrementális fejlesztés (a szoftvert növeszteni kell, nem építeni). Nagyszerű tervezők felkutatása és nevelése.

Mai alkalmazás. Az AI asszisztens a járulékos bonyolultságot csökkenti drámaian (szintaxis, boilerplate, API részletek). A lényegi bonyolultságot nem, a négy tulajdonság (bonyolultság, megfelelés, változékonyság, láthatatlanság) érintetlen. Ebből három szabály következik.


15. Kernighan és Plauger, a Unix filozófia

The Elements of Programming Style (1974, 2. kiadás 1978)

Brian Kernighan és P. J. Plauger könyve tankönyvekből vett, valódi FORTRAN és PL/I programrészleteket ír át jobbá, és minden átírásból egy rövid szabályt von le. A szabályok tartalmilag, csoportosítva.

TerületSzabály tartalma
VilágosságVilágos, egyszerű és közvetlen kód a túlzott okosság helyett. A világosság előbbre való a hatékonyságnál
KönyvtárakA könyvtári függvények használata az újraírásuk helyett
VáltozókCsak a szükséges ideiglenes változók maradnak. Minden változó deklarált
VezérlésFentről lefelé olvasható kód, a nyelv strukturált szerkezeteivel. Nincs kusza vezérlés és else ág nélküli bonyolult feltételes lánc
IsmétlésAz ismétlődő kifejezés függvénybe kerül. Egy modul egy dolgot csinál jól
AdatAz adatszerkezet határozza meg a program szerkezetét. Bonyolult logika helyett táblázat
Rossz kódA rossz kód foltozása és kommentelése helyett újraírás
MegjegyzésekA megjegyzés és a kód egyezik, a megjegyzés pedig többet mond, mint a kód
BemenetA bemenet hihetőségének és érvényességének ellenőrzése. A bemenet nem lépheti át a program korlátait. A hibás bemenet azonosítása, és ha lehet, továbblépés rajta
HatárokFigyelem az eggyel elcsúszott hibákra, a határértékek tesztelése. A program üres bemenetre is helyesen viselkedik
LebegőpontLebegőpontos számok egyenlőségvizsgálata csak óvatosan. Tízszer 0,1 ritkán egyenlő 1,0 értékkel
HatékonyságElőbb helyesség, aztán sebesség. Optimalizálás csak mérés után, és a gyorsítás közben is helyes marad a kód. A hatékonyságot jobb algoritmus hozza, nem a kód piszkálása
OlvashatóságA telefonpróba. Amit telefonon nem lehet érthetően felolvasni, azt át kell írni

Mai alkalmazás. Ez a lista szinte változtatás nélkül használható AI generált kód átnézésére. A legtöbbet érő pontok ma a következők. Foltozás helyett újraírás (ha egy generált függvényt harmadszor javítunk, jobb tiszta specifikációból újragenerálni). A megjegyzés és a kód egyezzen (a modell hajlamos olyan kommentet hagyni, amely egy korábbi változatot ír le). Táblázat a bonyolult logika helyett, ahogy a döntési táblánál láttuk.

Software Tools (1976)

Kernighan és Plauger második könyve Ratfor nyelven (strukturált FORTRAN előfeldolgozó) kis, egymással kombinálható eszközöket épít fel (szűrők, fájlösszehasonlítás, rendezés, szövegszerkesztő, makróprocesszor). Fő tanulsága, hogy egy jó eszköz egy dolgot csinál, szöveget olvas és ír, nem tesz fel kérdéseket, és más eszközökkel csővezetékbe köthető.

A Unix filozófia

A Unix 1969-ben indult a Bell Labsnél (Ken Thompson, Dennis Ritchie), a csővezeték (pipe) Doug McIlroy kezdeményezésére 1973-ban került be. McIlroy 1978-ban a Bell System Technical Journal Unix különszámának előszavában foglalta össze a stílus elveit, tartalmilag így.

  1. Egy program egy feladat, jól megoldva. Új igényre új program készül, a régi nem hízik új funkciókkal.
  2. Minden kimenet egy későbbi, még ismeretlen program bemenete lehet. Ezért a kimenet tömör, kerüli a merev oszlopos és a bináris formát, és nem vár interaktív választ.
  3. Korai kipróbálás, akár heteken belül, nagy rendszernél is. Az ügyetlen részek eldobása és újraépítése természetes.
  4. Eszközépítés a munka könnyítésére, akkor is, ha az eszköz megírása kitérőt jelent.

Thompson és Ritchie 1974-es CACM cikke (The UNIX Time-Sharing System) a rendszer egyszerűségét mutatja be. Egységes fájl absztrakció (eszköz, csővezeték, fájl ugyanazzal a felülettel), kevés rendszerhívás, szöveg mint univerzális formátum.

Mai alkalmazás.


16. Knuth. TAOCP és literate programming

The Art of Computer Programming (1. kötet, 1968)

Donald E. Knuth főműve 1968-ban indult (1. kötet, Fundamental Algorithms), a 2. kötet 1969-ben, a 3. 1973-ban jelent meg. A módszertani újdonság az algoritmusok elemzésének kultúrája. Minden algoritmus lépésenként számozott, természetes nyelvű leírással (Algorithm E, E1, E2, ...), MIX gépi kóddal, helyességi érveléssel és a futási idő pontos elemzésével szerepel. A gyakorlatokhoz nehézségi szint tartozik.

Átvehető gyakorlat. Az algoritmus először számozott lépésekben, természetes nyelven, és csak utána kódban. Minden lépésnél világos, mi a bemenete és mi a következő lépés.

Algoritmus E (Euklidész). Adott m, n pozitív egészek, keressük a legnagyobb közös osztót.
E1. [Maradék.] Osszuk m-et n-nel, r legyen a maradék (0 <= r < n).
E2. [Nulla?] Ha r = 0, az algoritmus véget ér, n a válasz.
E3. [Csere.] Legyen m <- n, n <- r, és menjünk vissza E1-re.

Knuth 1974-es cikke (Structured Programming with go to Statements) a goto vitát árnyalta, és itt szerepel a korai optimalizálásról szóló híres megállapítás. A teljes gondolat az, hogy az idő nagyjából 97 százalékában a kis hatékonyságok felett érdemes elsiklani, a kritikus 3 százalékot viszont mérés alapján meg kell találni és optimalizálni.

Literate programming (1984)

Knuth Literate Programming (The Computer Journal, 1984) cikke azt javasolta, hogy a programot elsősorban embernek írjuk, esszéként, amelyben a kódrészletek a magyarázat logikája szerint követik egymást, nem a fordító által megkövetelt sorrendben. A WEB rendszerben egy forrásból két eszköz dolgozik. A tangle a fordítható kódot, a weave a nyomtatható dokumentumot állítja elő. Maga a TeX is így készült.

Mai alkalmazás. A Jupyter notebook és a dokumentációs kód (doctest) közeli rokon. AI asszisztált munkánál a leghasznosabb következmény az, hogy a nem triviális modul tetején egy bekezdésnyi magyarázat áll arról, mit old meg és milyen döntéseket hozott (Parnas titka), a kód pedig ebből a gondolatmenetből következik.


17. Bentley. Programming Pearls és a borítékhátoldal számítás

Jon Bentley a CACM Programming Pearls rovatát 1983 és 1985 között írta (a rovat később is folytatódott), a könyv 1986-ban jelent meg, a második kiadás 1999-ben. Témái a probléma pontos megfogalmazása, az algoritmustervezés, a helyesség és a teljesítmény.

Borítékhátoldal számítás (back of the envelope)

A becslés gyors, nagyságrendi számítás, mielőtt bármit építenénk.

Lépések.

  1. A kérdés számszerűsítése (kérés per másodperc, bájt per nap, költség per hónap).
  2. A tényezők felírása kerekített értékekkel, egységekkel.
  3. A szorzás, a nagyságrend megtartásával.
  4. Ellenőrzés egy független úton (más tényezőkből, vagy egy ismert referenciával).
  5. Biztonsági tényező, Bentley javaslata szerint kettő, négy vagy hat, a bizonytalanság mértékétől függően.

Hasznos ökölszabályok.

Példa mai feladatra. Egy LLM alapú ügyfélszolgálati funkció havi költsége.

Napi beszélgetés:         500
Üzenet beszélgetésenként: 6
Token üzenetenként:       bemenet 3000 (kontextus + előzmény), kimenet 400
Havi bemeneti token:      500 * 6 * 3000 * 30 = 270 millió
Havi kimeneti token:      500 * 6 * 400 * 30  = 36 millió
Költség:                  token * egységár, a szolgáltató aktuális árlistájából
Ellenőrzés:               egy valódi beszélgetés naplójából mért token szám
Biztonsági tényező:       2 (hosszabb beszélgetések, újrapróbálás)

A számítás fél perc, és kiderül belőle, hogy a kontextus hossza a költség meghatározó tényezője, tehát ott kell optimalizálni (prompt cache, összefoglalt előzmény).

További gyöngyszemek

Mai alkalmazás. Architekturális javaslat (adatbázis választás, cache, sor, skálázás) előtt borítékhátoldal számítás készül, és a szám bekerül a döntés indoklásába. Ha a becsült terhelés napi néhány ezer kérés, nincs szükség elosztott rendszerre.


18. Naur. Programming as Theory Building (1985)

Peter Naur 1985-ös cikke (Microprocessing and Microprogramming folyóirat) Gilbert Ryle filozófus elméletfogalmára épít. A programozás lényege nem a programszöveg előállítása, hanem egy elmélet kiépítése a programozók fejében arról, hogyan felel meg a program a valós világ ügyeinek. Ez az elmélet magyarázza, miért olyan a program, amilyen, és hogyan kell módosítani, amikor a világ változik.

Következmények Naur szerint.

Mai alkalmazás. Ez a vibecoding legfontosabb figyelmeztetése. Ha a kódot a gép írja, és senki nem építi fel róla az elméletet, akkor a programnak a megszületése pillanatától nincs gazdája. Ebből három gyakorlat következik.

  1. Nem triviális változtatásnál a döntés oka is megfogalmazódik, nem csak a diff kerül át a csapathoz.
  2. A projekt gyökerében egy rövid, karbantartott leírás él a rendszer elméletéről (például docs/architecture.md, vagy az AI kódoló eszköz projektszintű instrukciós fájlja). Mi a cél, milyen titkokat rejtenek a modulok, milyen döntések születtek és miért.
  3. Módosítás előtt az elméletet visszaolvassuk (a leírást és a releváns kódot), és ha a kért változtatás ellentmond neki, ezt kimondjuk.

19. NATO konferenciák és Lehman törvényei

NATO Software Engineering konferenciák (1968, 1969)

Az első konferencia 1968 októberében Garmisch-Partenkirchenben volt, ennek nevéből terjedt el a software engineering kifejezés, szándékosan provokatív címként. A jelentést Peter Naur és Brian Randell szerkesztette. A második 1969 októberében Rómában zajlott, jelentését Buxton és Randell szerkesztette.

Tanulságok.

Lehman törvényei a szoftver hosszú távú változásáról

Meir M. Lehman az IBM OS/360 kiadásainak méréséből indult az 1960-as évek végén. Az első három törvényt 1974-ben fogalmazta meg, a rendszerezést 1980-ban adta közre a Proceedings of the IEEE hasábjain, a nyolc törvényes alak az 1990-es évek közepére állt össze.

Programtípusok (1980).

A nyolc törvény tartalmilag.

TörvényTartalom
I. Folyamatos változásEgy használatban lévő E rendszert folyamatosan módosítani kell, különben egyre kevésbé lesz megfelelő
II. Növekvő bonyolultságA változtatások növelik a bonyolultságot, hacsak nem dolgozunk tudatosan a csökkentésén
III. ÖnszabályozásA rendszer hosszú távú fejlődése statisztikailag szabályos, önszabályozó folyamat
IV. A szervezeti stabilitás megőrzéseAz átlagos tényleges fejlesztési ütem a rendszer élettartama alatt nagyjából állandó
V. Az ismertség megőrzéseKiadásonként a növekmény mérete nagyjából állandó, mert a résztvevőknek követniük kell a változást
VI. Folyamatos növekedésA funkcionalitásnak folyamatosan nőnie kell a felhasználói elégedettség megtartásához
VII. Csökkenő minőségA minőség csökkenőnek érzékelhető, hacsak a rendszert nem tartják szigorúan karban és igazítják a környezethez
VIII. Visszacsatolt rendszerA rendszer fejlődése többszintű, többhurkos visszacsatolt folyamat

Mai alkalmazás. A II. törvény az AI generált kód legnagyobb kockázata. A generálás olcsó, ezért a növekmény mérete (V. törvény) nem korlátozódik természetesen, és a bonyolultság gyorsabban nő, mint ahogy bárki megérti. Ezért minden nagyobb funkció után egy kis tisztító lépés következik (halott kód törlése, ismétlés összevonása, elnevezések egységesítése), és a kért változtatás a lehető legkisebb diffel készül.


20. Royce és Boehm. Vízesés, költséggörbe, COCOMO, spirál

Royce (1970). Amit valójában írt

Winston W. Royce Managing the Development of Large Software Systems (IEEE WESCON, 1970) cikkét a vízesésmodell forrásaként idézik, pedig a cikk a lineáris modellt kockázatosnak és kudarcra hajlamosnak nevezi. A cikk 2. ábrája a lépcsőzetes fázissort mutatja (rendszerkövetelmények, szoftverkövetelmények, elemzés, programtervezés, kódolás, tesztelés, üzemeltetés), utána Royce azt írja, hogy ez a megvalósítás kockázatos, mert a tesztelésnél derül ki először, hogy a terv működik-e, és ha nem, a követelményekig kell visszamenni.

Royce öt javaslata a kockázat csökkentésére.

  1. A programtervezés jöjjön először. Egy előzetes programterv már az elemzés előtt, amely az erőforrások (tár, idő) korlátait feltárja.
  2. Dokumentáld a tervet. Bőségesen, mert a dokumentáció maga a terv. Royce szerint egy dokumentáció nélküli terv nem terv.
  3. Csináld kétszer. Egy kísérleti változat (pilot) a végleges előtt, rövidebb idő alatt, hogy a kritikus kérdések kiderüljenek. Ez Brooks eldobható rendszerének előzménye.
  4. Tervezd, kontrolláld és figyeld a tesztelést. Tesztelési szakemberek, a kód vizuális átnézése (nem csak futtatása), minden logikai ág kipróbálása.
  5. Vond be az ügyfelet. Formálisan és mélyen, több ponton a fejlesztés során, nem csak az elején és a végén.

A cikk ábrái visszacsatoló nyilakat is mutatnak a szomszédos fázisok között. A tisztán lineáris vízesés későbbi leegyszerűsítés, részben a DoD-STD-2167 (1985) szabvány hatására terjedt el.

Mai alkalmazás. A csináld kétszer elv a vibecoding egyik legerősebb mintája. Egy gyors, eldobható prototípus a kockázatos részre (külső API viselkedése, teljesítmény, felhasználói élmény), majd a tanulságok alapján tiszta újraírás. A prototípus kódját nem visszük tovább csak azért, mert már megvan.

Boehm. Hibajavítási költség fázisonként

Barry W. Boehm Software Engineering Economics (1981) könyve és korábbi cikkei (1976, Software Engineering, IEEE Transactions on Computers) közölték a híres görbét. Egy követelményhiba kijavításának költsége annál nagyobb, minél később találják meg. Nagy projektekben a követelményfázisban talált hibához képest az üzemeltetésben talált hiba nagyjából százszor annyiba kerül, kis projekteknél a szorzó jóval kisebb (inkább öt körüli). Boehm és Basili 2001-es összefoglalója megerősítette a nagy projektekre vonatkozó nagyságrendet, és hozzátette, hogy a hibák kis része (nagyjából 20 százaléka) okozza az újramunka nagy részét.

Fázis, ahol a hibát megtaláljákRelatív költség (nagy projekt, nagyságrend)
Követelmények1
Tervezés3 és 6 között
Kódolás10
Fejlesztői teszt15 és 40 között
Átvételi teszt30 és 70 között
Üzemeltetés40 és 1000 között

A konkrét számok projektenként nagyon eltérnek, a görbe alakja a tanulság.

Mai alkalmazás. AI asszisztált fejlesztésnél a kódolás olcsóbb lett, a görbe alja így látszólag lapul. A követelmény és adatmodell hibák költsége viszont nem csökkent, mert az éles adat, a már kiküldött email, a kiszivárgott rekord nem generálható újra. A legtöbb kérdést ezért a generálás előtt érdemes feltenni, az adatmodellre, a jogosultságra és a visszafordíthatatlan műveletekre koncentrálva.

COCOMO (1981)

A Constructive Cost Model a program méretéből (ezer forrássor, KLOC) becsli a ráfordítást (emberhónap) és az időtartamot. Az alapmodell képletei.

Ráfordítás (E) = a * KLOC^b         emberhónap
Időtartam  (D) = c * E^d            hónap
Létszám        = E / D

Projekttípus     a     b      c     d
Organikus       2.4   1.05   2.5   0.38   kis csapat, ismert terület
Félig kötött    3.0   1.12   2.5   0.35   vegyes tapasztalat
Beágyazott      3.6   1.20   2.5   0.32   szigorú hardveres, szabályozói korlátok

Példa. Egy 32 KLOC organikus projekt. E = 2.4 · 32^1.05, ami nagyjából 91 emberhónap. D = 2.5 · 91^0.38, nagyjából 14 hónap, átlagosan 6 és 7 fő közötti létszámmal.

A közbülső COCOMO 15 költségtényezővel (cost drivers, például megbízhatósági igény, termék bonyolultsága, csapat képessége, eszközök) szorozza az alapbecslést, a részletes modell fázisonként és modulonként számol. A COCOMO II (1995 után, 2000-ben könyvben) funkciópontokkal és újrafelhasználással is számol.

Mai alkalmazás. A KLOC alapú becslés AI generált kódnál értelmét veszti, mert a sorok száma nem a munka mértéke. A tanulság a b kitevő. Ha nagyobb egynél, a ráfordítás a mérettel szuperlineárisan nő (diszeconomy of scale). A kódbázis méretét tehát tudatosan kicsinek kell tartani, és a generált kód mennyiségét költségként kell kezelni.

Spirálmodell (1986, 1988)

Boehm 1986-ban az ACM SIGSOFT Software Engineering Notes hasábjain, 1988-ban az IEEE Computerben publikálta (A Spiral Model of Software Development and Enhancement). A fejlesztés ismétlődő ciklusokban halad, minden kör négy negyedből áll.

  1. Célok, alternatívák, korlátok meghatározása az adott körre.
  2. Az alternatívák értékelése, a kockázatok azonosítása és feloldása, jellemzően prototípussal, szimulációval, méréssel.
  3. Fejlesztés és ellenőrzés a következő szintű termékre (koncepció, követelmény, terv, kód).
  4. A következő kör tervezése és áttekintés az érintettekkel, döntés a folytatásról.

A spirál sugara a felhalmozott költség, a szöge a haladás. Kockázatvezérelt módszer. Minden körben az a munka jön, amely a legnagyobb megmaradt kockázatot csökkenti.

Mai alkalmazás. Egy AI asszisztált munkamenet természetesen spirál. A szabály az, hogy minden kör elején megnevezzük a legnagyobb nyitott kockázatot (ismeretlen API, adatmodell bizonytalanság, teljesítmény), és azt a kört arra fordítjuk. A biztos, unalmas részek a végére maradnak.


21. Myers. The Art of Software Testing (1979)

Glenford J. Myers könyve a tesztelést a hiba megtalálásának céljaként definiálta. Egy sikeres teszt az, amelyik hibát talál. Ebből lélektani következmény adódik. A programozó ne a saját programját tesztelje, mert öntudatlanul a működést akarja bizonyítani (Weinberg gondolatával összhangban).

Fekete doboz technikák lépésenként.

  1. Ekvivalenciaosztályok (equivalence partitioning). A bemeneti tartományt osztályokra bontjuk, amelyeken belül a program várhatóan egyformán viselkedik. Minden feltételhez érvényes és érvénytelen osztályok tartoznak. Ha a mennyiség 1 és 999 között lehet, az osztályok a következők. Érvényes 1 és 999 között, érvénytelen 1 alatt, érvénytelen 999 felett, érvénytelen nem szám.
  2. Határérték elemzés (boundary value analysis). Az osztályok szélein és közvetlenül mellettük tesztelünk. 0, 1, 2, 998, 999, 1000.
  3. Ok és hatás gráf (cause effect graphing). A bemeneti okok és a kimeneti hatások logikai kapcsolatából döntési tábla, abból tesztesetek.
  4. Hibasejtés (error guessing). Tapasztalat alapján a tipikus hibahelyek (üres lista, null, nulla, ismétlődő elem, ékezetes karakter, időzóna váltás).

Fehér doboz lefedettségi szintek a gyengétől az erősig. Utasításlefedettség, döntéslefedettség (minden ág), feltétellefedettség (minden részfeltétel igaz és hamis), döntés és feltétel lefedettség, többszörös feltétel lefedettség (minden kombináció).

Mai alkalmazás. Tesztgeneráláskor előbb az ekvivalenciaosztályok és a határértékek listája készül el, és minden osztályra legalább egy eset jut. Az elvárt eredmény a specifikációból jön, nem a kód lefuttatásából, különben a teszt a hibát is rögzíti.


A gyakorlatban

A papírkorszak módszereinek mai, AI asszisztált fordításai, mindegyik a forrásával, hogy a döntést a csapaton belül és a megrendelő felé is meg lehessen indokolni.

Generálás előtt (papírfázis)

  1. Egy bekezdéses feladatleírás és IPO lap (McCracken, HIPO). Minden nem triviális függvény vagy végpont előtt felírjuk a bemeneteket (forrással), a kimeneteket (a mellékhatásokkal együtt, például adatbázis írás, email, külső hívás) és a lépéseket. Ha egy kimenetet egy lépés sem állít elő, a terv hibás.
  2. Döntési tábla három vagy több feltételnél (CODASYL). A teljes 2^n kombinációt kitöltjük és összevonjuk, a nem egyértelmű oszlopokat tisztázzuk. A kód ágai és a tesztesetek a tábla oszlopaiból jönnek.
  3. Adatszótár a kód előtt (DeMarco). A mezők, típusok, kötelezőség, határok (1{tétel}50) előbb Zod, Pydantic vagy SQL sémaként, csak utána a logika.
  4. Kontextusdiagram új rendszernél (DeMarco). Minden külső entitás és adatfolyam egy Mermaid ábrán. Ez egyben a bizalmi határok térképe.
  5. Változó döntések listája modulbontás előtt (Parnas). Melyik szolgáltató, formátum, szabály változhat, és melyik modul rejti el. Egy várható változás egy fájlt érintsen.
  6. Utófeltétel előbb, előfeltétel abból (Dijkstra wp). A függvény garanciáját fogalmazzuk meg először, és ebből vezetjük le, mit kell a bemeneten ellenőrizni.
  7. Bemenet és kimenet szerkezete a feldolgozó előtt (Jackson). Parser, importáló, csoportosító riport előtt felrajzoljuk mindkét szerkezetet, és megkeressük a szerkezeti ütközést (sorrendi, határ, átfedési).
  8. Borítékhátoldal számítás architektúra előtt (Bentley). Terhelés, adatmennyiség, LLM tokenköltség számmal, biztonsági tényezővel. Elosztott megoldást csak a szám indokolhat.
  9. A legnagyobb kockázat megnevezése (Boehm spirál, Royce csináld kétszer). Ha van ismeretlen, egy eldobható prototípus jön rá, és a tanulság után tiszta megvalósítás.

Generálás közben

  1. Lépésenkénti finomítás (Wirth). Előbb a váz (szignatúrák, üres törzsek), majd törzsenként finomítás, minden lépés után futtatással. Egyszerre több száz soros, ellenőrizetlen blokk nem készül.
  2. Strukturált vezérlés (Dijkstra). Korai return, lapos elágazás, kimerítő switch vagy match explicit hibával a maradék esetre, rejtett mellékhatás nélkül.
  3. Csatolás és kohézió (Yourdon és Constantine). Vezérlő boolean paraméter helyett két függvény, utils gyűjtőfájl helyett funkcionális modulok, a szükséges adat átadása, hétnél több hívott elemnél közbülső szint. Az import gráf körmentes (Parnas uses).
  4. Ciklusinvariáns kommentben (Floyd, Hoare, Gries). Minden nem triviális ciklus fölé INV és VARIANS megjegyzés, ahol hasznos, debug assert-tel.
  5. Koncepcionális integritás (Brooks). Új kód előtt megnézzük a meglévő mintákat (elnevezés, hibakezelés, adatelérés), és azokat követjük.
  6. Kis diff (Lehman II. és V. törvény). A kért változtatás a legkisebb módosítással, mellékes átírások nélkül. A szükséges tisztítás külön javaslatként érkezik.
  7. Unix felület szkripteknek és eszközöknek (McIlroy). Szöveg vagy JSON Lines be és ki, hiba stderr-re, kilépési kód, egy eszköz egy feladat.

Generálás után (asztali ellenőrzés és inspekció)

  1. Nyomkövető tábla a kritikus ciklusra (desk checking). Két-három bemenetre, köztük egy határesetre (üres, egy elem, maximum), a kód mellett táblázatként. Ha a tábla és a szándék eltér, a kód javítandó.
  2. Felolvasói kör (Fagan). Saját szavainkkal, soronként elmondjuk, mit csinál a kód, vagy egy új menetben az AI asszisztenssel mondatjuk el. Ahol a parafrázis és a szándék eltér, ott hiba van.
  3. Fagan stílusú önátnézési checklist külön menetben, a hibák listázásával a javítás előtt.
TerületKérdés
AdathivatkozásMinden érték definiált olvasás előtt? Null, undefined, üres lista, üres string kezelve?
Határok< vagy <=? Első és utolsó elem, üres bemenet, lapozás utolsó oldala?
SzámításPénz egész fillérben vagy decimálisban, nem lebegőpontban? Kerekítés iránya? Egész osztás?
IdőUTC és timestamptz? Időzóna, nyári időszámítás, hónap vége?
VezérlésMinden ciklus befejeződik? Minden ág kezelve? Újrapróbálásnak van felső korlátja?
InterfészParaméterek sorrendje, mértékegysége, visszatérési érték ellenőrizve? Külső hívásnak van időkorlátja?
HibakezelésNincs elnyelt kivétel? A hiba a hívóhoz vagy a naplóba jut, értelmes üzenettel?
JogosultságMinden végpont ellenőrzi, ki hívja és mihez fér hozzá? Sorszintű szabály az adatbázisban is?
BemenetMinden kívülről jövő adat (űrlap, webhook, LLM kimenet, fájl) sémával validált?
IdempotenciaKétszer lefutva sem duplikál (webhook, fizetés, ütemezett feladat)?
ErőforrásKapcsolat, fájl, stream lezárva? Nincs N+1 lekérdezés ciklusban?
MegjegyzésA kommentek a jelenlegi kódot írják le, nem egy korábbi változatot?
TitkokNincs kulcs, token, jelszó a kódban vagy a naplóban?
  1. Tesztesetek ekvivalenciaosztályból és határértékből (Myers). Előbb az osztályok listája, az elvárt eredmény a specifikációból, nem a kód futásából.
  2. Rossz kódot újraírni, nem foltozni (Kernighan és Plauger). Ha ugyanazt a generált részt harmadszor kell javítani, tiszta specifikációból újrageneráljuk.
  3. A program elméletének rögzítése (Naur, Parnas és Clements). Nem triviális változásnál a döntés oka is megfogalmazódik, és a projekt leírásában (docs/architecture.md, ADR, az AI kódoló eszköz instrukciós fájlja) frissül a rendszer elmélete a végső, rendezett formában, az elvetett alternatívákkal együtt.
  4. A bizonytalanság kimondása (Dijkstra, a tesztelés korlátja). Amit nem ellenőriztünk (nem futtattuk, nem teszteltük, csak feltételezzük), azt külön kimondjuk. A sikeres futás nem bizonyítja a hiba hiányát.

Arányérzék

A módszerek ára a feladat kockázatához igazodik. Egy egyszeri, eldobható szkriptnél elég a 10., 16. és 23. szabály. Üzleti logikánál, pénzt, személyes adatot vagy jogosultságot érintő kódnál a teljes lista érvényes. A papírkorszak programozója sem inspektált minden sort, de jól tudta, melyik sor hibája kerül egy hétbe.

Állapot 2026 októberében.

Források