2023/06/04

COBOL, GnuCOBOL, and Legacy Migrations

COBOL Programming

Increasingly, I've been working with legacy systems that are dependent on COBOL. One major thing that a large organization like CMS is trying to do is migrate a decades old systems into the Cloud. Being a part of an undertaking like this unfortunately requires being able to read and understand some COBOL.

From my research, it seems like CMS has experimented with a few different approaches to begin this migration. Probably the most ambitious one seems to have been an effort to translate their decades old codebase from COBOL to Java. Unsurprisingly, a one-size-fits-all approach like this just doesn't work (and it didn't). The largest flaw was that it seemed like there were some minute differences from doing calculations with high-precision floats that ended up compounding downstream. These differences were ultimately caused by foundational differences in how Java and COBOL store highly-precise numeric values.

Of course, there are other issues with doing a simple COBOL to Java conversion. The primary one is that converting a codebase from one language to another does not promise to change the STRUCTURE of that codebase. You ultimately just end up with another codebase that inherits the same bugs, quirks, and issues with maintainability as the old one. IMO, this effort was doomed to fail.

I am not suggesting that I have a better solution, but a clear learning from this is that you can't simply wave a wand and end up with a shiny new modern codebase. One approach that I would like to experiment with is combining GnuCOBOL and FFIs (Foreign Function Interfaces) to create a programming layer between COBOL and a modern programming language (ideally Rust). GnuCOBOL is able to compile a COBOL program to a C library that can be linked to, and most applications offer FFIs to interface with C.

Once a reliable layer exists, you can create connective tissue to restructure the legacy codebase and eventually just replace the FFI components with new reimplementations. Of course, I'm making this sound far easier than it is. Ultimately. this process requires a deep understanding of how disparate parts of the legacy code relate to one another. This is an extremely daunting task given the fact that these systems have been developed across decades, by developers who undoubtedly had varying perspectives about how to manage complex systems.

Alas, all programming journies begin with "Hello World", so let's try just that!

Let's say I have this COBOL program:

IDENTIFICATION DIVISION.
PROGRAM-ID.    TESTCOBC.
AUTHOR.        Ferris Tseng.

ENVIRONMENT DIVISION.

DATA DIVISION.

LINKAGE SECTION.
01 L-FNAME PIC X(10).

PROCEDURE DIVISION USING L-FNAME.
    DISPLAY 'Hello World! Hello 'L-FNAME.
    GOBACK.

So, what's going on here?

A COBOL application is structured into a series of divisions, each with a distinct purpose. The first, the IDENTIFICATION DIVISION simply stores some metadata about the program, like an application name and author. The ENVIRONMENT DIVISION stores some info about the environment the application is running on. The DATA DIVISION stores, well data like local variables, and the PROCEDURE DIVSION stores instructions.

Some important things to call out:

  1. The LINKAGE SECTION declares inputs into the program, so for this application, we have one alphanumeric string with 10 bytes. When compiling this to C, GNUCobol creates an entrypoint function with 1 argument.
  2. The end of the PROCEDURE DIVISION has a GOBACK. directive which transfers control back to the parent (kind of like a return).

Then, you can compile it to some C source files:

$ cobc -C -o out/test.c test.cob

Then, calling it from C is as easy as importing it and initializing the COBOL runtime!

#include "out/test.c"
#include <libcob.h>
#include <stdio.h>

int main(void) {
  cob_init(0, NULL);

  printf("Hello World from C!\n");
  TESTCOBC("SHEN NONG ");
  printf("After\n");

  cob_stop_run(0);
}
$ cobc -x testc.c -o out/main && ./out/main
Hello World from C!
Hello World! Hello SHEN NONG
After