COBOL 102: Building a To-Do List and Escaping the Modern Web


If you look up a tutorial for any modern JavaScript framework—React, Vue, Svelte—the "Hello World" project is always a To-Do list.

To build it, the modern web developer downloads Node.js, spins up a package manager, installs Webpack, configures a Virtual DOM, and pulls down 400MB of nested dependencies. All of this just to save a few strings of text and check if a date has passed. It is the absolute pinnacle of the Cobra Effect we talked about recently: engineering complexity instead of engineering solutions.

Let's do it the way the banks do it. Let's build a To-Do list in COBOL.

In the last post, I mentioned that mainframes hate interactive prompts, and that their true power lies in file processing. Today, we are going to bridge that gap. We will build an interactive local program using GnuCOBOL that asks for input, but saves and reads the tasks using raw, enterprise-grade Sequential File I/O. We will also add a feature to check if a task is overdue based on the system date.

1. The Setup: Defining the File

In COBOL, you don't just open a file stream on the fly. You have to declare the physical file mapping in the ENVIRONMENT DIVISION and describe the exact byte layout of the file in the DATA DIVISION.

We are using LINE SEQUENTIAL organization. This means the file is saved as standard text with line breaks, meaning you can open the resulting todos.dat file in vim or notepad and read it directly.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
      * OPTIONAL means it won't crash if the file doesn't exist yet
           SELECT OPTIONAL TODO-FILE ASSIGN TO "todos.dat"
           ORGANIZATION IS LINE SEQUENTIAL.

       DATA DIVISION.
       FILE SECTION.
       FD  TODO-FILE.
       01  TODO-RECORD.
           05 TODO-DUE-DATE   PIC 9(8).
           05 TODO-TEXT       PIC X(50).

Notice the layout: 8 bytes for the date (YYYYMMDD), and 50 bytes for the text. No JSON overhead. No XML tags. Just exactly 58 bytes of data per record.

2. The Logic: Handling Time and I/O

To check if a task is overdue, we need to know the current date. COBOL has a built-in function called CURRENT-DATE that returns a 21-character string containing the date, time, and timezone. We only care about the first 8 characters (YYYYMMDD).

Here is the complete program. Save this as todo.cbl.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TODO-APP.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT OPTIONAL TODO-FILE ASSIGN TO "todos.dat"
           ORGANIZATION IS LINE SEQUENTIAL.

       DATA DIVISION.
       FILE SECTION.
       FD  TODO-FILE.
       01  TODO-RECORD.
           05 TODO-DUE-DATE   PIC 9(8).
           05 TODO-TEXT       PIC X(50).

       WORKING-STORAGE SECTION.
       01  WS-CHOICE          PIC 9 VALUE 0.
       01  WS-EOF             PIC X VALUE 'N'.
       01  WS-CURRENT-DATE    PIC 9(8).
       01  WS-TEMP-DATE-TIME  PIC X(21).
       01  WS-OVERDUE-MSG     PIC X(10) VALUE SPACES.

       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
      * Extract the current YYYYMMDD from the system
           MOVE FUNCTION CURRENT-DATE TO WS-TEMP-DATE-TIME.
           MOVE WS-TEMP-DATE-TIME(1:8) TO WS-CURRENT-DATE.

           PERFORM UNTIL WS-CHOICE = 3
               DISPLAY " "
               DISPLAY "--- COBOL TO-DO LIST ---"
               DISPLAY "1. ADD A TASK"
               DISPLAY "2. VIEW TASKS"
               DISPLAY "3. EXIT"
               DISPLAY "CHOICE: " WITH NO ADVANCING
               ACCEPT WS-CHOICE

               IF WS-CHOICE = 1
                   PERFORM ADD-TASK
               ELSE IF WS-CHOICE = 2
                   PERFORM VIEW-TASKS
               END-IF
           END-PERFORM.

           DISPLAY "SYSTEM HALTED. GOODBYE.".
           STOP RUN.

       ADD-TASK.
      * OPEN EXTEND appends to the file instead of overwriting it
           OPEN EXTEND TODO-FILE.
           DISPLAY "ENTER DUE DATE (YYYYMMDD): " WITH NO ADVANCING.
           ACCEPT TODO-DUE-DATE.
           DISPLAY "ENTER TASK DESCRIPTION: " WITH NO ADVANCING.
           ACCEPT TODO-TEXT.
           
           WRITE TODO-RECORD.
           CLOSE TODO-FILE.
           DISPLAY "[+] TASK SAVED TO DISK.".

       VIEW-TASKS.
           DISPLAY " "
           DISPLAY "--- YOUR TASKS ---".
           OPEN INPUT TODO-FILE.
           MOVE 'N' TO WS-EOF.

      * The classic COBOL read loop
           PERFORM UNTIL WS-EOF = 'Y'
               READ TODO-FILE
                   AT END 
                       MOVE 'Y' TO WS-EOF
                   NOT AT END
                       IF TODO-DUE-DATE < WS-CURRENT-DATE
                           MOVE "[OVERDUE]" TO WS-OVERDUE-MSG
                       ELSE
                           MOVE "          " TO WS-OVERDUE-MSG
                       END-IF
                       DISPLAY TODO-TEXT " " WS-OVERDUE-MSG
               END-READ
           END-PERFORM.
           CLOSE TODO-FILE.

3. Compiling and Running

Compile it using GnuCOBOL just like we did in the last tutorial:

cobc -x -o todo todo.cbl
./todo

When you add a task, the OPEN EXTEND command grabs the file and positions the pointer at the very end so you can safely append data.

When you view your tasks, OPEN INPUT locks the file for reading. The READ statement pulls exactly 58 bytes from the disk, maps them directly into the memory layout we defined in the FD section, and executes the logic. Because dates are formatted strictly as YYYYMMDD, checking if a task is overdue is a simple mathematical less-than comparison (<). No complex Date-Object parsing required.

This is what the interaction looks like in your terminal:

--- COBOL TO-DO LIST ---
1. ADD A TASK
2. VIEW TASKS
3. EXIT
CHOICE: 1
ENTER DUE DATE (YYYYMMDD): 20260521
ENTER TASK DESCRIPTION: Update Papers for ID Card
[+] TASK SAVED TO DISK.
 
--- COBOL TO-DO LIST ---
1. ADD A TASK
2. VIEW TASKS
3. EXIT
CHOICE: 2
 
--- YOUR TASKS ---
Update Papers for ID Card                                    
 
--- COBOL TO-DO LIST ---
1. ADD A TASK
2. VIEW TASKS
3. EXIT
CHOICE: 3
SYSTEM HALTED. GOODBYE.

The Beauty of Sequential Files

If you add a few tasks and then exit the program, type cat todos.dat into your Linux or macOS terminal.

You won't see a serialized SQLite blob or a nested JSON tree. You will see raw, perfectly aligned ASCII text. This is how the global financial system processes your credit card transactions every night—reading fixed-width records line by line, at blistering speeds, with absolute deterministic reliability.

Next time, we will strip away the interactive menu entirely and build a true batch processor that reads a file, processes thousands of calculations, and generates an enterprise report.


← Back to COBOL & Z Server