Konverze byla dokončena za tři měsíce. Java aplikace se zkompilovala, prošla základními kouřovými testy a byla prohlášena za připravenou k nasazení. Po šesti měsících produkčního procesu si tým platformy všiml něčeho znepokojivého: nikdo v Java týmu nedokázal kód udržovat. Kontroly trvaly dvakrát déle než běžné kontroly Javy. Hlášení o chybách odkazovala na koncepty, WORKING-STORAGE, logiku PROCEDURE DIVISION, konvence balených decimalních polí, kterým vývojáři Javy bez zkušeností s mainframy nemohli rozumět. Aplikace byla převedena z COBOLu do Javy, ale výsledná Java myslela, strukturovala a chovala se jako COBOL. Byl to Jobol.
Jobol (někdy označovaný jako JOBOL) je souhrn kódu v Javě, který zachovává procedurální sémantiku COBOLu, aniž by využíval objektově orientované možnosti Javy. Jason Bloomberg, zakladatel analytické firmy Intellyx, jej definoval přímo: Jobol je technicky platný kód v Javě, který ponechává původní softwarovou architekturu a zachovává sémantiku COBOLu, což vyžaduje, aby vývojáři s Javou zacházeli, jako by to byl COBOL. Kritický důsledek plyne okamžitě: podnik, který převádí COBOL na Jobol, se nezbavil potřeby znalosti COBOLu. Jednoduše tento požadavek na znalost přesunul do jazyka, který nativně nepodporuje vzory COBOLu, které kód obsahuje. Vytvořil systém, který se hůře udržuje než původní COBOL a neposkytuje žádnou z očekávaných výhod Javy.
Graf závislostí před návrhem domény
SMART TS XL mapuje každou závislost PERFORM a CALL, takže vrstva služeb Java odráží skutečnou strukturu propojení, nikoli strukturu tříd COBOL.
ZJISTĚTE VÍCE…Jak Jobol ve skutečnosti vypadá
Než se diagnostikují příčiny, je třeba vidět vzorec. Zde je stejná obchodní logika, kontrola úvěruschopnosti zákazníka, ve třech formách: původní COBOL, Jobol, který obvykle produkují automatizované převodníky, a idiomatická Java, kterou vytváří kvalifikovaná modernizace.
Původní COBOL:
cobol
PERFORM-CREDIT-CHECK.
MOVE WS-CUST-ID TO DB-CUST-KEY
READ CUSTOMER-FILE KEY IS DB-CUST-KEY
INVALID KEY
MOVE 'NOT FOUND' TO WS-STATUS
PERFORM HANDLE-MISSING-CUSTOMER
END-READ
IF CUST-CREDIT-SCORE < 650
MOVE 'DECLINED' TO WS-CREDIT-RESULT
MOVE 'Score below threshold' TO WS-DECLINE-REASON
ELSE IF CUST-OUTSTANDING-BALANCE > CUST-CREDIT-LIMIT
MOVE 'DECLINED' TO WS-CREDIT-RESULT
MOVE 'Balance exceeds limit' TO WS-DECLINE-REASON
ELSE
MOVE 'APPROVED' TO WS-CREDIT-RESULT
END-IF.
Jobol produkovaný řádkovým převodníkem:
Jáva
// Jobol: procedural COBOL logic mapped directly to Java
// No OOP, no encapsulation, static global state, COBOL naming conventions
public class CreditCheckProgram {
static String WS_CUST_ID;
static String DB_CUST_KEY;
static String WS_STATUS;
static String WS_CREDIT_RESULT;
static String WS_DECLINE_REASON;
static int CUST_CREDIT_SCORE;
static double CUST_OUTSTANDING_BALANCE;
static double CUST_CREDIT_LIMIT;
public static void PERFORM_CREDIT_CHECK() {
DB_CUST_KEY = WS_CUST_ID;
Customer customer = CustomerFile.read(DB_CUST_KEY);
if (customer == null) {
WS_STATUS = "NOT FOUND";
HANDLE_MISSING_CUSTOMER();
return;
}
CUST_CREDIT_SCORE = customer.getCreditScore();
CUST_OUTSTANDING_BALANCE = customer.getOutstandingBalance();
CUST_CREDIT_LIMIT = customer.getCreditLimit();
if (CUST_CREDIT_SCORE < 650) {
WS_CREDIT_RESULT = "DECLINED";
WS_DECLINE_REASON = "Score below threshold";
} else if (CUST_OUTSTANDING_BALANCE > CUST_CREDIT_LIMIT) {
WS_CREDIT_RESULT = "DECLINED";
WS_DECLINE_REASON = "Balance exceeds limit";
} else {
WS_CREDIT_RESULT = "APPROVED";
}
}
}
Idiomatická Java, tedy kvalifikovaná modernizace, produkuje:
Jáva
// Idiomatic Java: domain model, encapsulation, value objects, Spring service
@Service
public class CreditCheckService {
private final CustomerRepository customerRepository;
public CreditCheckService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
public CreditDecision check(CustomerId customerId) {
Customer customer = customerRepository.findById(customerId)
.orElseThrow(() -> new CustomerNotFoundException(customerId));
if (customer.creditScore() < CreditThresholds.MINIMUM_SCORE) {
return CreditDecision.declined("Score below threshold");
}
if (customer.outstandingBalance().compareTo(customer.creditLimit()) > 0) {
return CreditDecision.declined("Balance exceeds limit");
}
return CreditDecision.approved();
}
}
Verze v Jobolu má 32 řádků statického globálního stavu, je procedurálně strukturovaná, pojmenovaná v COBOLu a nelze ji jednotkově testovat bez předchozí inicializace stavu globální třídy. Idiomatická verze má 20 řádků, je injektovatelná, testovatelná a okamžitě čitelná jakýmkoli vývojářem v Javě bez znalostí COBOLu. Obě implementují stejná obchodní pravidla. Pouze jedno z nich je udržovatelné týmem, kterému měla modernizace umožnit.
Šest vzorů Jobol a proč je konvertoři vytvářejí
Vzor 1: Globální statický stav nahrazující WORKING-STORAGE
Sekce WORKING-STORAGE v COBOLu obsahuje proměnné na úrovni programu, které přetrvávají i během volání PERFORM. Jedná se o sdílený proměnný stav programu, každý odstavec čte a zapisuje stejné proměnné WS. Automatizované převodníky mapují toto přímo na statická pole Java v jedné třídě, čímž zachovávají sémantiku sdíleného stavu a zároveň porušují všechny principy zapouzdření Java.
Proč to převodníky dělají: Mapování polí WORKING-STORAGE na statické je mechanická transformace jedna k jedné. Převodník nechápe, že model zapouzdření Javy nahrazuje sdílený stav na úrovni programu proměnnými instance, parametry metod a návratovými hodnotami. Převádí to, co vidí, aniž by chápal, proč původní struktura existovala.
Důsledek: Statický stav je izolovaně netestovatelný, bez explicitní synchronizace není bezpečný pro práci s danými vlákny a je strukturálně identický s COBOLem, který nahradil. Každý vývojář, který potřebuje do systému přidat souběžnost, musí nejprve rozmotat statický stav, tedy práci, která byla odložena z konverze na každého následujícího správce.
Oprava: Mapujte proměnné WORKING-STORAGE na pole instance, parametry metod nebo návratové hodnoty na základě jejich rozsahu použití. Proměnné použité pouze v jednom odstavci se stanou lokálními proměnnými. Proměnné předávané mezi odstavci se stanou parametry metod nebo návratovými hodnotami. Proměnné, které skutečně reprezentují stav sdíleného objektu, se stanou poli instance ve správně zapouzdřené třídě domény.
Vzor 2: Řetězce PERFORM se stávají dlouhými statickými metodami
Programy v COBOLu jsou organizovány jako řetězce PERFORM, kde hlavní procedura PERFORMS provádí sekvenci pojmenovaných odstavců, z nichž každý může provádět PERFORM i pro jiné odstavce. Automatizované převodníky převádějí každý odstavec na statickou metodu a každou PERFORM na volání statické metody. Výsledkem je jedna třída s desítkami statických metod, které se navzájem volají prostřednictvím grafu volání, který zrcadlí strukturu PERFORM v COBOLu.
Jáva
// Jobol: PERFORM chain mapped to static method calls
public class ProcessOrderProgram {
public static void main() {
validateOrder();
checkInventory();
calculateTotal();
applyDiscounts();
processPayment();
generateConfirmation();
updateInventory();
}
public static void validateOrder() { /* ... */ }
public static void checkInventory() { /* ... */ }
// ... 40 more static methods
}
Proč to převodníky dělají: Strukturální mapování z odstavce COBOLu na metodu Javy je přímé. Převodník správně identifikuje, že PERFORM znamená „zavolat tuto kódovou jednotku“. Co mu uniká, je to, že objektový model Javy organizuje kód kolem entit domény, nikoli kolem kroků zpracování.
Oprava: Identifikujte koncepty domény obsažené v řetězci PERFORM. validateOrder, calculateTotal, a applyDiscounts jsou operace na objednávce. processPayment je operace v platebním kontextu. checkInventory a updateInventory jsou operace na Inventory. Refaktoring distribuuje statické metody do doménových tříd, do kterých patří, a nahrazuje dlouhý řetězec procedurálních volání krátkou orchestrační metodou, která deleguje na příslušné doménové objekty.
Vzor 3: Číselné typy bez přesného zpracování
Pole COMP-3 (packed decimal (COMP-3)) v COBOLu mají přesnou desítkovou aritmetiku. Převodníky, které mapují COMP-3 na Javu double or float zavádějí systematické chyby zaokrouhlování s plovoucí desetinnou čárkou, které se hromadí v milionech finančních transakcí.
Jáva
// Jobol: COMP-3 mapped to double (wrong)
static double WS_TRANSACTION_AMOUNT; // Was PIC S9(9)V99 COMP-3
static double WS_RUNNING_TOTAL;
// After 10M additions of 0.1:
// Expected: 1,000,000.00
// Actual: 1,000,000.0000000002 (floating point accumulation)
Jáva
// Idiomatic Java: COMP-3 mapped to BigDecimal (correct)
private BigDecimal transactionAmount; // Preserves decimal precision
private BigDecimal runningTotal;
// After 10M additions of 0.1:
// Result: exactly 1,000,000.00
Proč to konvertoři dělají: double je výchozí číselný typ Javy. Převodník, který nerozumí specificky sémantice COMP-3, bude standardně používat nejběžnější číselný typ Javy, což je špatná volba pro přesnou desítkovou aritmetiku.
Oprava: Každé pole COMP-3 se mapuje na BigDecimalKaždý numerický výpočet, který zahrnuje hodnoty COMP-3, používá BigDecimal aritmetika. scale() z BigDecimal odpovídá implicitním desetinným místům v klauzuli PIC: PIC S9(9)V99 má 2 implicitní desetinná místa, mapující se na BigDecimal s měřítkem 2.
Vzor 4: Řetězce EBCDIC a relikty bajtového pole
COBOL zpracovává znaková data jako řetězce s pevnou délkou a doplněné mezerami v kódování EBCDIC. Převodníky často vytvářejí kód v Javě, který zachovává sémantiku řetězců s pevnou délkou a mezerami i po převodu na UTF-8, což vede k Javě, která zpracovává řetězce jako programátor sálových počítačů ze 1970. let.
Jáva
// Jobol: preserves COBOL fixed-length string handling
static char[] WS_CUSTOMER_NAME = new char[40]; // Was PIC X(40)
static char[] WS_CUSTOMER_ADDR = new char[80]; // Was PIC X(80)
// COBOL-style space-padding comparison
if (new String(WS_CUSTOMER_NAME).trim().equals("SMITH")) { ... }
// COBOL-style string concatenation with padding
System.arraycopy(name.toCharArray(), 0, WS_CUSTOMER_NAME, 0,
Math.min(name.length(), 40));
Jáva
// Idiomatic Java: proper String types and domain objects
private String customerName; // Trim on read, no padding
private String customerAddress;
// Clean comparison
if ("Smith".equalsIgnoreCase(customer.name())) { ... }
Proč to převodníky dělají: Definice COBOL PIC X(40) má délku, kterou převodník zachovává jako velikost pole v Javě. Převod EBCDIC na UTF-8 sice lze zvládnout, ale sémantika pevné délky se přenáší zbytečně.
Oprava: Mapa PIC X(n) pole do String objekty, ořezávání při čtení a odstraňování omezení pevné délky. Jediný kontext, kde omezení délky hrají roli, je na hranicích perzistence (délky sloupců databáze, pozice formátu souboru), nikoli v modelu domény.
Vzor 5: Ošetření chyb pomocí stavových kódů
COBOL sděluje chybové stavy pomocí stavových kódů: FILE STATUS, SQLCODE, RETURN-CODE. Převodníky tento vzor v Javě zachovávají a vytvářejí kód, který po každé operaci kontroluje celočíselné stavové kódy, místo aby používal model výjimek Javy.
Jáva
// Jobol: COBOL status code pattern in Java
static int WS_FILE_STATUS;
static int WS_SQL_CODE;
CustomerFile.read(DB_CUST_KEY);
WS_FILE_STATUS = CustomerFile.getLastStatus();
if (WS_FILE_STATUS == 10) {
// Not found
} else if (WS_FILE_STATUS == 35) {
// File not available
} else if (WS_FILE_STATUS != 0) {
// Unknown error
}
Jáva
// Idiomatic Java: exceptions for error conditions
try {
Customer customer = customerRepository.findById(customerId)
.orElseThrow(() -> new CustomerNotFoundException(customerId));
// process customer
} catch (DatabaseUnavailableException e) {
log.error("Database unavailable during credit check", e);
throw new CreditCheckServiceException("Service temporarily unavailable", e);
}
Proč to převodníky dělají: Kontrola stavového kódu je vzor COBOL; jeho převod na metodu Java, která vrací nebo nastavuje stavové pole, je mechanická transformace. Uznání, že výjimky Java jsou idiomatickým ekvivalentem, vyžaduje architektonický úsudek, nikoli mechanický překlad.
Oprava: Mapujte chybové podmínky COBOLu na výjimky Java. STAV SOUBORU 10 (záznam nenalezen) se změní na Optional.empty() nebo výjimku specifickou pro doménu. Chyby SQLCODE se stávají typovými výjimkami databáze. Struktura kódu Java odráží záměr, try/catch pro očekávané režimy selhání, nekontrolované výjimky pro neočekávané.
Vzor 6: Monolitická struktura třídy
Programy v COBOLu jsou monolitické, jeden program zvládá celou obchodní funkci. Převodníky vytvářejí pro každý program v COBOLu jednu třídu Java, čímž vznikají třídy Java, které mohou mít tisíce řádků a zvládat více úkolů, které by objektově orientovaný design rozdělil mezi několik specializovaných tříd.
Proč to převodníky dělají: Jeden program v COBOLu se mapuje na jednu třídu Java, jednotkou překladu převodníku je program. Vnitřní struktura programu (jeho odstavce, sekce, řetězce PERFORM) se stává vnitřní strukturou třídy bez rozkladu na menší třídy.
Řešení: Aplikujte princip jediné odpovědnosti na každou převedenou třídu. Třída Jobol o délce 3 000 řádků, která ověřuje vstup, čte z databáze, aplikuje obchodní pravidla, formátuje výstup a zapisuje do souboru, by se měla stát: validátorem vstupu, repozitářem, doménovou službou, formátovačem a zapisovačem, každý testovatelný samostatně, každý zaměřený na jednu odpovědnost.
Předpoklad pro strukturální analýzu
Každý vzor Jobol má odpovídající refaktoring. Refaktoring však nelze aplikovat převodníkem, je nutné porozumět tomu, co program dělá, než se rozhodneme, jak jej restrukturalizovat. Toto porozumění je přesně to, co poskytuje strukturální analýza zdrojového kódu v COBOLu před spuštěním jakéhokoli převodního nástroje.
Konkrétně strukturální analýza poskytuje vstupy, které rozhodnutí o konverzi vyžadují:
Inventář obchodní logiky. Kompletní výčet podmínek EVALUATE, větví IF a vzorců COMPUTE v každém programu, to, co program rozhoduje, nejen jak je to strukturováno. Tento inventář je specifikací, které musí refaktorovaná Java odpovídat. Program s 847 podmínkami EVALUATE vyžaduje strukturu cílové třídy, která pojme 847 rozhodovacích bodů; výstupem převodníku je počáteční koncept, nikoli hotový produkt.
Graf závislostí. Které programy volají které další programy. Řetězec PERFORM, který zahrnuje více programů v COBOLu, se v Javě stává grafem volání služeb. Graf závislostí určuje, které programy lze refaktorovat nezávisle (málo příchozích závislostí) a které musí být refaktorovány jako koordinované skupiny (mnoho sdílených závislostí prostřednictvím sešitů nebo sdílených dat).
Identifikace mrtvého kódu. Odstavce a sekce, které nejsou nikdy PROVEDENY z žádné produkční běhové cesty, jsou mrtvý kód, neměly by se vůbec objevit v převedené Javě. Převodníky, které obsahují mrtvý kód, nafukují převedenou kódovou základnu a následné refaktorování.
Obnova datového modelu. Položky VSAM FD, definice tabulek DB2 a struktury copybooku, které definují data programu. Každá datová struktura COBOL se mapuje na jeden nebo více objektů domény Java; mapování je určeno datovým modelem, nikoli názvy polí v WORKING-STORAGE.
| Analytický vstup | Rozhodnutí o konverzi, které řídí |
|---|---|
| Počet větví EVALUATE/IF | Dekompozice tříd domény, kolik rozhodovacích případů každá služba zpracovává |
| Počet příchozích závislostí | Sekvence refaktoringu, programy s nejmenším počtem závislostí jako první |
| Procento mrtvého kódu | Zmenšení rozsahu, vyloučení nedosažitelného kódu z konverze |
| Pole COMP-3 | BigDecimal mapování pro každé číselné pole |
| Délky polí PIC X | Mapování typů řetězců a omezení persistence |
| Struktura grafu PERFORM | Dekompozice vrstvy služeb, řetězce PERFORM se stávají voláními doménových služeb |
| Použití STAVU SOUBORU | Návrh modelu výjimek, které stavové kódy se mapují na které typy výjimek |
Co by měla konverze místo toho vyprodukovat
Alternativou k Jobolu je konverzní proces, který používá COBOL jako specifikaci, nikoli jako šablonu. Program v COBOLu zná obchodní logiku, program v Javě by ji měl vyjádřit v jazyce Javy.
Princip převodu: Převeďte záměr, ne syntaxi. COBOL
IF CUST-CREDIT-SCORE < 650 MOVE 'DECLINED' TO WS-CREDIT-RESULTříká „zákazník s kreditním skóre pod 650 je odmítnut.“ Java by měla v jazyce Javy říkat totéž:if (customer.creditScore() < CreditThresholds.MINIMUM_SCORE) return CreditDecision.declined(...). Neif (CUST_CREDIT_SCORE < 650) { WS_CREDIT_RESULT = "DECLINED"; }.
Praktický konverzní postup, který vytváří idiomatické jazyky v Javě:
Krok 1: Strukturální analýza zdrojového kódu COBOL, graf závislostí, inventarizace obchodní logiky, obnova datového modelu, identifikace nefunkčního kódu. Tento krok vytváří specifikaci. Dokud není tento krok dokončen, nic se nekonvertuje.
Krok 2: Návrh modelu domény, identifikace obchodních entit (Zákazník, Účet, Transakce, Politika), na kterých programy v COBOLu fungují. Ty se stanou třídami domény Java. Proměnné WORKING-STORAGE, které reprezentují stav entity, se stanou poli instance v příslušné třídě domény.
Krok 3: Návrh vrstvy služeb, identifikace obchodních operací (kontrola úvěruschopnosti, zpracování plateb, výpočet zůstatku), které implementují odstavce a sekce v COBOLu. Ty se stanou metodami služeb Java na příslušné třídě služeb domény. Řetězec PERFORM, který seskupuje odstavce, se stává orchestrační logikou služby.
Krok 4: Automatický převod jako generátor návrhů, použijte převodní nástroj k vytvoření výchozího návrhu Java pro každý program, na základě modelu domény navrženého v krocích 2 a 3. Zkontrolujte návrh oproti inventáři obchodní logiky, abyste ověřili, že je zastoupena každá rozhodovací větev.
Krok 5: Systematické refaktorování proti vzorům Jobol, použití šesti oprav proti vzorům: nahrazení statického stavu stavem instance, distribuce statických metod do doménových tříd, nahrazení stavových kódů výjimkami, nahrazení double s BigDecimal, nahraďte pole s pevnou délkou za String, rozložit monolitické třídy na služby s jednou odpovědností.
Krok 6: Testování behaviorální ekvivalence, spuštění refaktorované Javy s korpusem charakterizačních testů vygenerovaným z analýzy COBOL. Každá podmínka EVALUATE a větev IF by měla mít alespoň jeden testovací případ, který ji procvičuje a ověřuje ekvivalenci s výstupem COBOL.
Jak SMART TS XL Zabraňuje vzniku Jobolu u zdroje
Předpokladem strukturální analýzy pro prevenci Jobolu, graf závislostí, inventuru obchodní logiky, identifikaci mrtvého kódu a obnovu datového modelu je přesně to, co... SMART TS XLanalýza zdrojového kódu COBOLu, kterou poskytuje, před spuštěním jakéhokoli konverzního nástroje.
SMART TS XLJe statická analýza kódu vyjmenovává každou podmínku EVALUATE, každou větev IF, každý vzorec COMPUTE a každý vztah PERFORM ve zdrojovém kódu COBOL, kompletní inventář obchodní logiky, který musí refaktorovaná Java věrně reprezentovat. Když konverzní nástroj vygeneruje Jobol, inventář obchodní logiky je specifikací, vůči které je refaktorování ověřeno: každá podmínka v inventáři se musí objevit v Javě, v příslušné třídě domény, vyjádřené v jazyce Javy.
Mapování závislostí aplikací vytváří graf PERFORM a graf volání programu, které určují strukturu vrstvy služeb Java. Programy, které se navzájem volají, často patří do stejného ohraničeného kontextu. Programy, které jsou volány mnoha dalšími službami, jsou sdílené služby, které musí být pečlivě navrženy tak, aby podporovaly všechny své uživatele v architektuře Java. Graf závislostí je vstupem pro krok návrhu vrstvy služeb, který převádí implicitní strukturu propojení COBOLu na explicitní strukturu vkládání závislostí Javy.
Analýza modernizace staršího kódu identifikuje mrtvý kód, odstavce a sekce, které nebyly nikdy dosaženy z produkčních cest a které by měly být z rozsahu konverze zcela vyloučeny. Konverze mrtvého COBOLu na mrtvý Jobol a následná refaktorizace mrtvého Jobolu plýtvá úsilím, které krok identifikace mrtvého kódu eliminuje před zahájením konverze.
Funkce analýzy dopadů ověřuje sekvenci refaktoringu: když se rozhraní programu v COBOLu během refaktoringu z jazyka Jobol na idiomatickou Javu změní, analýza dopadů identifikuje všechny ostatní programy, jejichž ekvivalent v Javě musí být také aktualizován, aby zůstal konzistentní se změněným rozhraním. Refaktoring v pořadí závislostí, od nejmenšího počtu závislých programů napřed, minimalizuje koordinaci potřebnou v každém kroku.
Převodníky překládají syntaxi. Modernizace transformuje význam.
Automatizované převodníky z COBOLu do Javy jsou rychlejší než lidské přepisování. Jsou také optimalizovány pro jiný cíl: syntaktickou korespondenci spíše než sémantickou ekvivalenci v idiomu cílového jazyka. Jobol je to, co syntaktická korespondence vytváří, když zdrojový jazyk (COBOL) a cílový jazyk (Java) mají zásadně odlišné strukturální modely.
Organizace, které se vyhýbají Jobolu, nejsou ty, které používají lepší převodníky. Jsou to ty, které s převodníkem zacházejí jako s generátorem návrhů, nikoli jako s hotovým produktem, investují do strukturální analýzy před zahájením převodu, navrhují model domény Java před spuštěním převodníku a systematicky refaktorují výstup převodníku s inventářem obchodní logiky jako validační specifikací.
Jobolu se lze vyhnout. Cenou za jeho vyhnutí se je strukturální analýza a modelování domény, které by měly předcházet každé konverzi. Cenou za jeho nevyhnutí se je systém Java, jehož údržba vyžaduje znalost COBOLu, což znamená, že očekávaný přínos modernizace, tedy schopnost udržovat systém s vývojáři Javy, se nikdy nedosáhne.
Převeďte záměr. Ne syntaxi. Rozdíl spočívá v celé hodnotě modernizace.


