Architecture decision records
Architecture Decision Records[edit]
| Field | Value |
|---|---|
| Document ID | PGCBL-ADD-001 |
| Document Type | Architecture Decision Document |
| System | pgcobol — Prime Numbers Application v1.0 |
| Version | 1.0 |
| Status | Draft — decisions marked [INFERRED] need developer confirmation |
| Owner | Lead Developer / Architect |
| Author | [reverse-engineered from source artifacts] |
| Created | 2026-03-17 |
| Last modified | 2026-03-17 |
| Classification | Internal |
| Parent document | PGCBL-TSD-001 |
Version History[edit]
| Version | Date | Author | Status | Change Summary |
|---|---|---|---|---|
| 0.1 | 2026-03-17 | — | Draft | All ADRs inferred from source |
Table of Contents[edit]
- Purpose and Format
- ADR-001 — Three-Tier Architecture
- ADR-002 — Method-Dispatch Pattern
- ADR-003 — Divisors Stored in Database
- ADR-004 — GixSQL as SQL Pre-Processor
- ADR-005 — Copybooks as Interface Contracts
- ADR-006 — Hard-Coded Database Credentials
- ADR-007 — No Explicit COMMIT on INSERT
- ADR-008 — Odd-Only Sieve Starting at 3
- Open Issues
1. Purpose and Format[edit]
An Architecture Decision Record (ADR) captures a single significant design decision: the context that prompted it, the options that were considered, the choice that was made, and the consequences. ADRs are written once and never deleted — if a decision is reversed, a new ADR documents the reversal and links to the original.
This document follows the MADR (Markdown Architectural Decision Records) format, a widely adopted lightweight standard (see https://adr.github.io/madr/).
ADR template[edit]
## N. ADR-NNN — <Short title of the decision> **Date:** YYYY-MM-DD **Status:** Proposed | Accepted | Deprecated | Superseded by ADR-NNN **Deciders:** <names or roles> **Source:** [CONFIRMED] | [INFERRED from source] | [ASSUMED] ### Context <What situation or problem prompted this decision? What forces are at play?> ### Decision Drivers - <driver 1> - <driver 2> ### Options Considered | Option | Description | Pros | Cons | |--------|-------------|------|------| | A | ... | ... | ... | | B | ... | ... | ... | ### Decision <The option chosen and a one-line justification.> ### Consequences **Positive:** - <benefit 1> **Negative / trade-offs:** - <drawback 1> **Risks:** - <risk 1> ### Compliance check <How to verify this decision is being followed in the codebase.>
2. ADR-001 — Three-Tier Architecture[edit]
Date: 2025 (inferred)
Status: Accepted
Deciders: Original developer
Source: [INFERRED from source structure]
Context[edit]
A batch COBOL program could be written as a single monolithic program with all logic, SQL, and output in one place. This project chose to separate concerns into distinct compiled units.
Decision Drivers[edit]
- Demonstrate a maintainable, layered COBOL design pattern.
- Allow each tier to be tested, replaced, or modified independently.
- Keep SQL in one place so that database changes affect only one program.
Options Considered[edit]
| Option | Description | Pros | Cons |
|---|---|---|---|
| A | Single monolithic program | Simple; one file to manage | Hard to test; all concerns coupled |
| B | Three-tier (chosen) | Separation of concerns; independently testable tiers | More files; inter-program calling overhead |
| C | Two-tier (no UI layer) | Simpler than three | Print formatting coupled to business logic |
Decision[edit]
Option B — three-tier (presentation / business logic / data access), each as a separate compiled COBOL program. A fourth orchestrating program (primesmain) manages the session lifecycle.
Consequences[edit]
Positive: - SQL is isolated in primes.cbl; database changes do not affect other programs. - Print formatting is isolated in primesui.cbl; output format changes do not affect logic. - Each program can be called from a test harness independently.
Negative / trade-offs: - Four programs must be compiled and linked instead of one. - Inter-program calls carry a small runtime overhead. - A shared copybook change requires recompiling all programs that use it.
Compliance check[edit]
grep -l "EXEC SQL" *.cbl should return only primes.cbl.
grep -l "OPEN.*fprinter\|WRITE.*file-buffer" *.cbl should return only primesui.cbl.
3. ADR-002 — Method-Dispatch Pattern[edit]
Date: 2025 (inferred)
Status: Accepted
Deciders: Original developer
Source: [INFERRED from copybook design]
Context[edit]
COBOL does not have objects, interfaces, or function pointers in the traditional sense. When a caller invokes a subprogram, it can either call different entry points (ENTRY paragraphs) or pass a discriminator value and let the callee dispatch internally. The design must allow each subprogram to expose multiple operations without multiple entry points.
Decision Drivers[edit]
- Keep the inter-tier interface to a single
CALLstatement per tier. - Allow new operations to be added to a tier without changing the caller’s CALL syntax.
- Use a pattern that is idiomatic in COBOL and familiar to COBOL programmers.
Options Considered[edit]
| Option | Description | Pros | Cons |
|---|---|---|---|
| A | Separate CALL per operation (CALL "primes-connect", CALL "primes-fetch" …)
|
Explicit; no dispatch overhead | Many CALL statements; callers must know all entry names |
| B | ENTRY paragraph per operation (single program, multiple named entries) | Standard COBOL; compiler-enforced | Callers must still reference entry names; no single interface point |
| C | Method-dispatch over a control block (chosen) | Single CALL; extensible; copybook is the interface contract | Verb strings must be kept in sync between copybooks and EVALUATE |
Decision[edit]
Option C — a string verb in a copybook field (dal-methods, ui-methods, methods) drives an EVALUATE TRUE inside the called program. The copybook is the published interface; the 88-level conditions make the valid verbs self-documenting.
Consequences[edit]
Positive: - Adding a new operation requires only: a new 88-level condition in the copybook and a new WHEN clause in the callee. - The caller’s CALL statement never changes. - The copybook 88-levels serve as the API documentation.
Negative / trade-offs: - Verb strings are not type-checked at compile time; a misspelled verb silently falls through to the WHEN OTHER error branch. - Result codes (0/1/9/99) are numeric constants, not named conditions at the caller site — callers must remember the convention.
Risks: - Verb string drift: if a verb is renamed in the copybook but not in the callee’s EVALUATE, the operation silently does nothing and returns result 1.
Compliance check[edit]
Every verb listed in a dal-methods / ui-methods / methods 88-level must have a corresponding WHEN clause in the callee’s EVALUATE. Verify with a diff of copybook 88-values against callee WHEN values.
4. ADR-003 — Divisors Stored in Database[edit]
Date: 2025 (inferred)
Status: Accepted
Deciders: Original developer
Source: [INFERRED from algorithm and SQL design]
Context[edit]
The trial-division sieve needs a list of previously found primes to use as divisors. These could be held in a COBOL working-storage table (an array), in a flat file, or in the database.
Decision Drivers[edit]
- Demonstrate database interaction as part of the algorithm (pedagogical goal).
- Keep the COBOL working-storage footprint small — avoid a large in-memory prime array.
- Persist divisors so the report can run independently of the generation run.
Options Considered[edit]
| Option | Description | Pros | Cons |
|---|---|---|---|
| A | In-memory COBOL table | Fastest; no I/O | Fixed size; lost between runs; defeats the DB demonstration goal |
| B | Flat file | Persistent; simple | Requires file I/O; not the DB demonstration goal |
| C | Database (chosen) | Persistent; demonstrates DB use; divisors and output in same store | One SQL query per trial division step — potentially very slow |
Decision[edit]
Option C — each divisor is fetched from the primes table by ident using a point SELECT. Generated primes accumulate in the same table as the divisors.
Consequences[edit]
Positive: - Demonstrates the full three-tier pattern end-to-end. - All state is in the database; generation can be interrupted and (in principle) resumed.
Negative / trade-offs: - Performance is heavily dependent on the SELECT prime WHERE ident = :n query. Without an index on ident, this becomes a sequential scan that gets slower as the table grows. - The design requires prime 2 to be pre-seeded in the table before the algorithm starts, since the first divider fetch assumes ident=2 exists.
Risks: - No index on ident is defined in primes_schema.sql (see NFR-PERF-03). This is the most significant performance risk in the system.
Compliance check[edit]
Run \d primes.primes in psql; verify an index on ident is present. If absent, add: CREATE INDEX idx_primes_ident ON primes.primes (ident);
5. ADR-004 — GixSQL as SQL Pre-Processor[edit]
Date: 2025 (inferred)
Status: Accepted
Deciders: Original developer
Source: [CONFIRMED — explicit in compiler listing header and DATASRC prefix pgsql://]
Context[edit]
GnuCOBOL does not natively support embedded SQL. An external pre-processor is needed to translate EXEC SQL … END-EXEC blocks into COBOL CALL statements to a SQL runtime library.
Decision Drivers[edit]
- Open-source toolchain (no licensing cost).
- PostgreSQL support.
- Compatible with GnuCOBOL.
Options Considered[edit]
| Option | Description |
|---|---|
| ESQL/C bridge | Write SQL in C, call from COBOL — complex |
| OCESQL | Another open-source COBOL SQL pre-processor — less PostgreSQL-specific |
| GixSQL (chosen) | Open-source; PostgreSQL-optimised; active development |
Decision[edit]
GixSQL — translates EXEC SQL to GIXSQLExec, GIXSQLConnect, GIXSQLConnectReset calls. Connection string prefix pgsql:// is GixSQL-specific.
Consequences[edit]
Positive: - Standard embedded SQL syntax in COBOL source. - PostgreSQL-native driver.
Negative / trade-offs: - GixSQL-specific connection string prefix (pgsql://) and runtime library are not portable to other COBOL SQL pre-processors. - GixSQL is not part of the GnuCOBOL distribution; must be installed separately.
Compliance check[edit]
All SQL pre-processing must be done through GixSQL. Verify no raw CALL "GIXSQLExec" statements appear in handwritten source (only in generated .out files).
6. ADR-005 — Copybooks as Interface Contracts[edit]
Date: 2025 (inferred)
Status: Accepted
Deciders: Original developer
Source: [INFERRED from copybook design]
Context[edit]
In a multi-program COBOL system, data shared between programs can be defined redundantly in each program’s WORKING-STORAGE, or defined once in a copybook included by all parties.
Decision[edit]
Define each tier’s control block once in a copybook (primes-session.cpy, primes-dal.cpy, primes-ui.cpy) and COPY it into both the caller’s WORKING-STORAGE and the callee’s LINKAGE SECTION. The copybook is the published interface; changing it is a breaking change affecting all users.
Consequences[edit]
Positive: - Single source of truth for each interface. - Field layout guaranteed to match between caller and callee.
Negative / trade-offs: - Any field size change in a copybook requires recompiling every program that includes it. - Two nearly-identical host-variable copybooks exist (primes-table.cpy vs primes_table.cpy) — the canonical version must be designated and the other retired.
Compliance check[edit]
grep -r "COPY primes-dal" *.cbl — should list only primesgen.cbl (caller) and primes.cbl (callee).
7. ADR-006 — Hard-Coded Database Credentials[edit]
Date: 2025 (inferred)
Status: Accepted (as technical debt — see note)
Deciders: Original developer
Source: [CONFIRMED — visible in primes.cbl lines 14–17]
Context[edit]
Database credentials must be provided to the GixSQL CONNECT call. Options include hard-coding, environment variables, a configuration file, or a secrets manager.
Decision[edit]
Credentials are hard-coded in WORKING-STORAGE of primes.cbl as a pragmatic choice for a demonstration application.
⚠ Technical debt note: This decision is explicitly acknowledged as a security defect (NFR-SEC-01). It is documented here so that the decision is visible and the remediation can be tracked. It must not be carried forward to a production deployment.
Recommended remediation[edit]
Replace DBUSR and DBPWD VALUE clauses with:
<syntaxhighlight lang="cobol">ACCEPT DBUSR FROM ENVIRONMENT "PRIMES_DB_USER" ACCEPT DBPWD FROM ENVIRONMENT "PRIMES_DB_PASS"</syntaxhighlight> Or read from a protected configuration file at startup.
Consequences[edit]
Negative: - Credential rotation requires recompilation and redeployment. - Credentials are visible in the binary and in any version-control history. - Violates NFR-SEC-01 and OWASP Secure Coding Practice #3.
8. ADR-007 — No Explicit COMMIT on INSERT[edit]
Date: 2025 (inferred)
Status: Accepted (status uncertain — see note)
Deciders: Original developer
Source: [INFERRED — COMMIT statement is commented out in primes.cbl r83-write-prime]
Context[edit]
The INSERT in r83-write-prime does not issue an explicit COMMIT. The commented-out COMMIT suggests this was a deliberate choice or a deferred decision.
Decision[edit]
Rely on PostgreSQL’s default auto-commit behaviour for individual INSERT statements outside an explicit transaction. The COMMIT is not needed because each INSERT auto-commits.
⚠ Note: The report path opens an explicit transaction (
START TRANSACTION) vias01-cursor. If the generate path is ever run while a transaction is open (e.g. if the session management is changed), the INSERTs will not auto-commit. This is a latent risk.
Consequences[edit]
Positive: - Simpler code; no COMMIT overhead per row.
Risks: - If a transaction is ever opened on the primes connection before r83-write-prime, the INSERTs will not be committed until an explicit COMMIT or ROLLBACK, and data will be lost on failure.
9. ADR-008 — Odd-Only Sieve Starting at 3[edit]
Date: 2025 (inferred)
Status: Accepted
Deciders: Original developer
Source: [CONFIRMED — ADD 2 TO test-number in r82-next-test-number; initial value 3]
Context[edit]
The sieve must test whether each integer is prime. Testing every integer (2, 3, 4, 5 …) is correct but wasteful: all even numbers greater than 2 are composite and need not be tested.
Decision[edit]
Start the candidate at 3 and advance by 2 (ADD 2 TO test-number), testing only odd integers. Prime 2 is handled as a manual seed rather than by the algorithm.
Consequences[edit]
Positive: - Roughly halves the number of candidates that must be tested, improving performance.
Negative / trade-offs: - Prime 2 must be manually seeded in the database before the algorithm runs (see Assumption A-01). This is an external dependency on an operational step, not a code-enforced constraint.
Compliance check[edit]
Verify test-number is initialised to 3 (odd) and that only ADD 2 TO test-number (never ADD 1) advances the candidate in r82-next-test-number.
10. Open Issues[edit]
| ID | Issue | Owner | Target | Status |
|---|---|---|---|---|
| OI-01 | All ADRs marked [INFERRED] need confirmation from original developer | Lead Dev | — | Open |
| OI-02 | ADR-006 remediation (credentials) has no target date | Lead Dev | — | Open |
| OI-03 | ADR-007 (no COMMIT) — confirm the intent is auto-commit, not an oversight | Lead Dev | — | Open |
| OI-04 | Duplicate copybooks (ADR-005) — designate canonical version and retire the other | Developer | — | Open |
Terug naar: Design standards | Cobol and PostgreSQL