thebusinessbasic.com Free report

A working reference · the language, not a product

Business Basic.Still running the warehouse.

A business-oriented dialect of BASIC, designed in the late 1970s for keyed-file data processing on multi-user terminal systems. It was never fashionable and it never stopped working. Somewhere near you, a pick ticket is printing because of it right now.

This page is what the language actually is, where it came from, why it outlived the hardware it was written for, what the code looks like, how the files work — and, at the end, an honest comparison of what you can do about it if you still run one.

We publish this because we run on it. Our own orders go through a Business Basic ERP.

Published by Unique Business Basic
A Unique Photo company · Fairfield, New Jersey · in business since 1947.
We also sell a Business Basic runtime, which you should know before you read our comparison of the options. It is option three. The rest of this page stands on its own.

tty014 · 80×24 · character terminal

Three illustrative 80-column screens: an order-entry line-item screen, a program listing showing a keyed read, and a file-information screen showing the key structure of a multi-keyed customer master.

Illustrative screens showing a Business Basic application, its source, and its file structure.

The application, the source behind it, and the file underneath — the three things every Business Basic shop lives with. Screens are illustrative, drawn to be representative rather than copied from any one product.

01 What it actually is

A language built for one job: the company's records, on a shared machine, at a dozen terminals at once.

Business Basic is not one product. It is a family of BASIC dialects that appeared on minicomputers in the late 1970s, aimed squarely at commercial data processing: order entry, inventory, receivables, payroll, general ledger. Several vendors shipped one. They were similar enough to feel like the same language and different enough that moving between them was real work.

What they all share is the job description, and the job description explains everything else. The language was not designed to teach programming or to compute trajectories. It was designed so that a small team could write an order-entry program that reads a customer record in one statement, edits a field on a screen in another, adds a line to an invoice without losing a penny to binary rounding, and keeps doing all of that while eleven other terminals are in the same file.

Judged against that brief, it is a good design. Judged against a brief nobody gave it — web interfaces, relational reporting, package management — it looks dated. Most writing about it makes the second comparison. The first one is more useful.

Keyed files are part of the language

You open a file on a numbered channel and read a record by its key in a single statement. The index lives inside the file; the access method is a verb. No driver, no connection string, no query planner, no round trip to another process.

Arithmetic is decimal, and exact

Numbers are held as decimal digits, not binary floating point, and a PRECISION setting fixes how many places results carry. Money behaves the way the accounting department assumes it does. This is not a small thing to inherit.

The screen is addressed from the print statement

Cursor positioning and display attributes are written inline, in the same statement that prints the text. Full-screen forms, windows and highlighted fields, with no UI toolkit and no layout engine — just a program writing to a grid of characters.

Multi-user was the starting assumption

Record-level locking is built into the file access verbs. Reading a record for update takes the lock; writing it back releases it. Concurrency was never bolted on later, because the machine always had twenty people typing at it.

Error handling is a branch on the statement

"If there's no record for that key, go to line 900." Missing keys, end of file and I/O failures each get their own branch clause on the statement that might cause them. Local, explicit, and decades ahead of the alternative at the time, which was nothing.

The program is the schema

Field names, types and widths are declared in the program — as a templated string or a named I/O list — not in a catalog beside the data. That makes the file self-contained and portable, and it makes "what is actually in this file?" a question you answer by reading code.

Clearing this up first

What it is not

It is not Visual Basic, and it is not the macro language in a spreadsheet. It shares an ancestor with them — BASIC, from Dartmouth in 1964 — and almost nothing else. It is not a scripting language wrapped around someone else's database; the file system is the database. It is not slow in the way people assume interpreted languages are: the interpreter runs tokenized code in a tight loop and the dominant cost is disk, which is why these systems felt fast on hardware that would be embarrassed by a modern phone.

And it is not a toy. There are payroll, tax, costing and allocation routines written in this language that have been correct for longer than most software companies have existed.

02 Where it came from

Four eras, and one of them is the reason it's still here.

Dates below are deliberately given as decades. The individual products have precise histories; the arc is what matters, and the arc is the same everywhere.

011970s

The minicomputer shipped with a BASIC in the box.

A department could buy one machine with a handful of terminals hanging off it. The vendor supplied the operating system, the terminals and the language — and the language was extended with the things a business needed: indexed files, formatted screens, decimal arithmetic. Software houses wrote accounting and distribution packages in it and sold them alongside the hardware.

  • Keyed files
  • Green screens
  • Packaged accounting

021980s–90s

The runtime left the hardware. This is the pivot.

As general-purpose operating systems spread, the interpreters were reimplemented on them — and the applications came along untouched. A package written for a minicomputer that no longer existed kept running on new machines because only the layer underneath it had been replaced. That single move is why this software is still in production, and it is exactly the move being made again today.

  • Ported interpreters
  • Same source
  • New hardware

031990s–2000s

The industry moved. These systems mostly didn't.

Relational databases, client/server, the web, then software as a service — each wave assumed the previous generation would be replaced. Some of it was. A great deal of it was audited for the year-2000 rollover instead, found to be fixable, fixed, and put straight back into service. There were standardization efforts along the way; in practice each implementation kept its own extensions and portability between dialects stayed partial.

  • Y2K audit
  • Repaired
  • Returned to service

04Today

A virtual machine, an SSH session, and a license renewal.

The typical installation now runs on a modest server or VM, is reached with a terminal emulator over SSH, and is licensed per seat by whoever owns the interpreter. The application has been extended continuously for decades. The people who extended it are retiring. Nothing about the software is failing — the situation around it is what has changed.

  • One server
  • Per-seat license
  • Thin bench

03 Why it survived

It survived because it worked, and because replacing it was always the more expensive mistake.

This is the part that gets written about badly. The usual explanation is inertia, or fear, or managers who don't understand technology. Spend a week inside one of these systems and a much less flattering explanation appears: the arithmetic was right.

The programs encode rules nobody wrote down

Which customer gets which price on which day. How a backorder is split across two warehouses. Which freight terms suppress the fuel surcharge. What the tax exception is for that one municipality. Forty years of edge cases, each added because something went wrong once. The code is the only complete statement of how the business actually operates — and a rewrite has to rediscover every one of them, from people who mostly no longer work there.

The runtime is portable, so the hardware dying didn't kill it

Because the application only ever talked to the interpreter, replacing the machine underneath it was somebody else's problem. Three or four generations of hardware have passed under these programs without them being edited. That decoupling is the single most underrated property of the whole arrangement.

For the people who use it all day, it is faster

No pointer, no page load, no focus surprises, deterministic tab order, and every field reachable from the home row. An experienced order-entry clerk on a 24-line screen enters lines faster than the same person on the web application meant to replace it. This is not nostalgia; it's measurable, and it's why the users are frequently the ones defending the green screen.

It is extremely cheap to run

A whole company on hardware you would now consider a rounding error. No application server, no container platform, no cache tier, nothing to patch on a Tuesday. The operating cost of the software itself rounds to the electricity — which is precisely what makes the per-seat license stand out on the invoice.

The replacement business case never closed

A rewrite reproduces what you already have, at seven figures and several years, and returns no new revenue for doing it. Every year it is deferred, the deferral is individually rational. That is not stupidity; it is a series of correct decisions that add up to a system nobody chose deliberately.

And exit is expensive by construction

The interpreter is licensed per seat by a single supplier and the application can't run without it. That is a strong commercial position, and it isn't a conspiracy — it's the natural shape of a market where the switching cost was always assumed to be a rewrite. It is worth naming plainly, because it's half of why the invoice keeps arriving.

What actually is wrong, then

Respecting the software does not mean pretending the situation is fine. Almost everything that worries people about these systems sits around the code rather than in it:

The concernIs it the language?What it really is
One server, no DRNoAn infrastructure decision made when a second machine was a capital project. Nothing about the software prevents replication, snapshots or a tested restore.
The license line itemNoA commercial arrangement with the runtime vendor. The programs are yours; the interpreter is rented.
Nobody left who knows itPartlyThe language is small and learnable in weeks. The application — the accumulated rules — is what takes years, and that would be true in any language.
Hard to integratePartlyThere is no SQL surface by default, so ecommerce, EDI, carrier and tax integrations get glued on with extracts and interface files. Workable, fragile, and everyone's least favorite subsystem.
Reporting is painfulYesAd-hoc reporting against keyed files means writing a program or exporting a copy. This is a genuine limitation of the file model, not a matter of taste.
Source resists modern toolingYesPrograms are stored tokenized rather than as text, so version control, diffing and code search need an export step. It shapes how teams work more than any other item on this list.

The useful question is which column your problem is in. Two of these six are properties of the language and the file model. Four of them are properties of the box, the contract and the org chart — and those four are the ones people describe as "our legacy system problem".

The software isn't the problem. Everything around it is.

Order entry written before the people running it were born still ships product every day. That is an awkward fact for anyone selling a replacement, and it's the fact any honest assessment has to start from.

04 What the code looks like

Five samples. If you've written this, you'll recognize it in the first line.

These are illustrative — written for this page to show the shape of the language, not copied out of anyone's product. Spelling varies a little between dialects; the shapes do not. Every construct is explained underneath the sample it appears in.

Sample 01Read one customer record by key
0100 REM  CUSTOMER MASTER INQUIRY
0110 OPEN (1,ERR=9000) "ARCUST"
0120 DIM C$:"CUSTNO:C(6),NAME:C(30),CITY:C(20),ST:C(2),CRLIM:N(10.2),BAL:N(10.2)"
0130 K$="100482"
0140 READ (1,KEY=K$,DOM=0900) C$
0150 PRINT C.NAME$,"  ",C.CITY$,", ",C.ST$
0160 IF C.BAL>C.CRLIM THEN GOSUB 0700
0170 CLOSE (1); STOP
0900 REM  no record on that key
0910 PRINT "CUSTOMER NOT ON FILE"; GOTO 0130
OPEN (1,ERR=9000)
Files are opened on a small numbered channel, not into a handle variable. Every later statement refers to the file by that number — which is why channel numbers are a scarce, carefully documented resource in a large application, usually with a comment block at the top of the program listing them.
DIM C$:"…"
A templated string: the record layout, declared in the program. C is character data of the given width, N is numeric with an implied decimal position. The template is the closest thing to a schema definition, and it lives here rather than in a catalog.
C.NAME$ / C.BAL
Once a record has been read into the templated string, its fields are addressed by name. Character fields take the string suffix; numeric fields don't. The parsing is done by the runtime against the template, so the program never computes an offset by hand.
READ (1,KEY=K$)
Read the record whose key matches. This is a descent of the index that lives inside the file — the reason a lookup in a file of well over a million records is a couple of milliseconds and not a scan.
DOM=0900
"If there is no record for that key, continue at line 900." The not-found case is a branch on the statement that might cause it. ERR= catches other failures and END= catches end of file the same way.
Line numbers
Not decoration — they are the addressing scheme. Branch targets, I/O list references and error handlers are all line numbers, which is why programs are written with conventional blocks: 0100 for setup, 0900 upward for error paths, high numbers for subroutines and I/O lists.
Sample 02The same record, declared as an I/O list
0200 READ (1,KEY=K$,DOM=0900) IOL=8000
0210 BAL=BAL+AMT
0220 WRITE (1,KEY=K$) IOL=8000
0230 STOP
8000 IOLIST CUSTNO$,NAME$,CITY$,ST$,CRLIM,BAL
IOLIST … IOL=
An I/O list names the variables that make up a record, once, at one line number. Every read and write in the program then refers to that line. Change the record layout in one place and every statement that touches the file follows.
Why two ways?
Templated strings and I/O lists solve the same problem from different directions: the template describes a record as one string with named fields, the I/O list describes it as an ordered set of ordinary variables. Large applications use both, often in the same program, and which one you meet depends on who was writing in which decade.
The consequence
Neither one is stored with the file. Two programs can hold different opinions about the layout of the same record, and the file will not object. This is the single most important thing to understand about the data model, and it's why "just point a reporting tool at it" is harder than it sounds.
Sample 03A screen — cursor positioning, attributes, function keys
0300 PRINT 'CS'
0310 PRINT @(0,0),'BR'," SALES ORDER ENTRY",@(62,0),"SOE110  CO 01 ",'ER'
0320 PRINT @(2,3),"CUSTOMER #:",@(15,3),'CL'
0330 INPUT (0,SIZ=6,ERR=0320) K$
0340 IF CTL=4 THEN EXITTO 9500
0350 IF CTL=2 THEN CALL "SOL110",K$
0360 IF K$="" THEN GOTO 0320
@(col,row)
Cursor addressing, written inline in the print statement. The screen is a grid of characters and the program writes to coordinates in it. A full-screen maintenance form is a few dozen of these, usually generated from a painted layout.
'CS' 'CL' 'BR' 'ER'
Two-letter mnemonics in single quotes: clear screen, clear to end of line, begin and end reverse video. The runtime translates them into whatever the attached terminal understands, from a capability table — which is how the same program drives a serial terminal from 1984 and a modern emulator without knowing the difference. The exact vocabulary varies by dialect; the mechanism doesn't.
INPUT (0,SIZ=6)
Channel zero is the terminal. A sized input gives you a fixed-width field with a character-level editor — insert, delete, overtype, arrow keys — for free, which is why data entry on these screens feels tight rather than improvised.
CTL
A system variable holding a code for the key that ended the input. This is how function keys are read: not an event handler, just a number you check on the next line. F-key conventions (help, lookup, prior, exit) are consistent across a well-built application and are burned into the muscle memory of everyone who uses it.
CALL "SOL110"
Runs another program and returns. Applications are built as many small programs rather than few large ones, partly because memory was scarce and partly because a program per screen is a genuinely clear structure.
Sample 04Money — decimal arithmetic and picture masks
0400 PRECISION 4
0410 EXT=QTY*PRC
0420 DSC=EXT*DPC/100
0430 NET=EXT-DSC
0440 PRINT @(56,LN),USING "----,--#.##",NET
0450 TOT=TOT+NET
PRECISION 4
Sets how many decimal places results are carried and rounded to. Arithmetic is decimal, not binary floating point, so a tenth is exactly a tenth and a line extension times a discount percentage lands where an accountant expects it to. Change this setting and a company's numbers change — it is one of the few global switches in the language and it is treated with appropriate suspicion.
USING "----,--#.##"
A picture mask. # is a digit position, - is a digit position that suppresses leading zeros and floats the minus sign out to the left, and the comma and point are literal. Formatting is part of the print statement, which is why these reports line up perfectly and why nobody ever needed a formatting library.
Why it matters
Half a century of financial software has been written on this arithmetic model. Any system that intends to run these programs has to reproduce the rounding behaviour exactly, in every operation, or the differences surface at month end as a few cents that take a week to find.
Sample 05Locking, updating, and reading through an alternate key
0500 EXTRACT (1,KEY=K$,DOM=0900,ERR=0500) IOL=8000
0510 BAL=BAL+AMT
0520 WRITE (1,KEY=K$) IOL=8000
0530 REM  the record lock is released by the write
0600 REM  browse customers in name order instead
0610 READ (1,KEY="SMITH",KNUM=1,DOM=0690) IOL=8000
0620 READ (1,END=0690) IOL=8000
0630 GOTO 0620
EXTRACT
Read a record and take an exclusive lock on it. The lock is held until the write puts it back. Locking is per record and enforced by the file system, so two terminals can work in the same customer file all afternoon and only collide if they touch the same customer.
What you don't get
There is no transaction. No begin, no commit, no rollback across two files. If a program dies between the invoice write and the ledger write, the two files disagree, and putting them back into agreement is the job of a recovery program somebody wrote a long time ago. Every shop has one. Every shop knows exactly who understands it.
KNUM=1
Read through alternate key one instead of the primary. A multi-keyed file carries several indexes, each assembled from pieces of the record, and the file system maintains all of them on every write. This is what makes "look this customer up by name" a single statement.
READ with no key
Reads the next record in the order of the current key, so a browse is a read in a loop with END= to catch the end. Sequential access in key order is the second thing the file model is very good at.

The vocabulary, at a glance

ConstructWhat it doesWhy it's there
OPEN / CLOSEAttach a file to a numbered channel, and detach itThe channel number is the file's identity for the rest of the program
READRead a record — by key, or the next one in key orderThe workhorse. Indexed access as a single statement
EXTRACTRead and lock, for updateMulti-user correctness without a transaction manager
FINDRead requiring an exact key matchSays "this must exist" in the statement rather than in a comment
WRITEAdd or replace a record, maintaining every indexAlternate keys stay correct because the file system rebuilds them
REMOVEDelete the record for a keySpace is returned to the file's own free list, not to the disk
KEY= / KNUM=Which key value, and which index to useOne file, several orders of access
DOM= / END= / ERR=Branch targets for missing key, end of file, and failureError handling attached to the statement that can fail
IOLIST / IOL=Name a record's fields once; refer to them everywhereThe nearest thing to a schema declaration
DIM x$:"…"Templated string with named, typed, fixed-width fieldsRecord layout and field access in one declaration
PRINT @(c,r)Position the cursor and writeFull-screen forms without a UI framework
'CS' 'CL' 'BR'Terminal mnemonics, translated per terminal typeOne program, many terminals, across four decades
INPUT / SIZ=Read a fixed-width field from the terminalA character-level field editor you didn't have to write
CTLCode for the key that terminated the inputHow function keys are read
PRECISIONDecimal places carried and rounded toExact money arithmetic, globally consistent
USING "…"Picture mask for formatted outputReports and screens that align to the column
CALL / ENTERRun another program; receive its argumentsApplications built from many small programs
SETERR / ESCAPESet a general error handler; leave itA last line of defence around the branch clauses

Names and exact syntax differ between dialects — this is the common core, and it is small. A competent programr reads it fluently within a week. What takes years is not the language; it is the ten thousand business decisions the programs contain.

05 The file model

There are no tables. There are files, and the files have indexes in them.

If you come from SQL, this is the part that requires actually changing your mental model rather than translating vocabulary. It is not a worse relational database. It is a different thing that was very good at what it was asked to do.

A file is a file: one object in a directory, holding fixed-length records and, for a keyed file, one or more indexes maintained inside the same object. There is no server process in the middle. The program's process opens the file and reads it, and the operating system's locking keeps everyone honest.

A record is a row of bytes. Its shape is described in the program that reads it — the template or the I/O list from the samples above — and nowhere else. There is no catalog, no data dictionary the file system consults, no declared constraint, no foreign key, no type checking at the file boundary. The file will happily store whatever you write to it.

Keys and segments

A keyed file can carry several indexes. The primary key is usually the obvious identifier — customer number, item number, order number. The alternate keys are assembled from segments: pieces of the record identified by offset and length, concatenated in order, sometimes with a descending flag so that "most recent first" is a plain sequential read. The file screen in the terminal above shows exactly this: key two is zip code then name; key three is salesperson then customer number.

The file system rebuilds every affected index on every write. That is why a write costs more than a read, why a file with six alternate keys is slower to load than one with two, and why the index rebuild utility exists and gets run at two in the morning after something went wrong.

Denormalized on purpose

The customer record holds the aging buckets. The item record holds quantity on hand, quantity committed, quantity on order. These are not computed at read time from a transaction history — they are maintained by the programs at write time, incremented and decremented as things happen. That is why an inquiry screen answers instantly on modest hardware, and it is also why a program that dies halfway through leaves a number wrong, and why every one of these systems has a nightly or monthly routine that recalculates the totals and reports the differences.

Once you see that, a lot of the application's design stops looking primitive and starts looking like a deliberate trade: cheap reads, expensive writes, and recovery handled by convention rather than by the storage engine.

What the model is good at, and what it isn't

Very good at

Fetching one record by key, in a couple of milliseconds, out of a file with millions of records. Walking records in key order. Adding a record without a schema migration. Running on almost nothing. Being copied with the ordinary file tools your backup already uses.

Not built for

Joins, aggregates and set operations — you write a program that opens both files and does it in a loop. Ad-hoc questions from someone who isn't a programr. Declarative constraints. Multi-file transactions. Anything that assumes the data describes itself.

File typeShapeTypically used for
SerialRecords one after another, no indexInterface files, extracts, print images, anything read start to finish
DirectFixed-length records addressed by numberControl records, counters, small fixed tables
IndexedOne key, records addressed by itThe straightforward master file
Multi-keyedSeveral keys, each built from segmentsCustomer, item, order and vendor masters — everything the application looks up more than one way
Sort / workKey-only or scratch files built by a jobReport sequencing, batch selection, the middle of a month-end routine
String / programVariable-length text, and tokenized programsNotes and descriptions; the application's own source library

Names differ by dialect. What matters is that they are all ordinary files on ordinary storage — which is why these systems back up cleanly with the tools you already have, and why moving them between machines has always been a copy rather than a migration.

06 The ecosystem

The language is half of it. The rest is what grew around the language.

Anyone planning to do anything with one of these systems needs to know about all six of these, because the interpreter is rarely the part that surprises you.

The terminal

Eighty columns, twenty-four rows, and a capability table that maps the language's mnemonics onto the escape sequences a given terminal type understands. Real serial terminals became emulators; the programs never noticed. Function-key conventions and box drawing are part of the contract with the user, which is why "close enough" on a data-entry screen is a training cost rather than a cosmetic issue.

Per-seat runtime licensing

The interpreter is licensed by concurrent user or by named seat, usually annually, and the application package is licensed separately on top. Adding a warehouse adds seats. This is the line item that makes finance ask what the alternative is, and it is entirely separate from the question of whether the software works.

The report writer and the spooler

Reports go to a device by alias, not to a printer by address. The aliases live in a configuration file, along with device classes, form control and hold queues. Output is 132 columns wide because the printer was, and the print subsystem is invariably the part of the system with the most local knowledge baked into it and the least documentation.

The configuration file

One file typically defines terminals, printers, device aliases, file path prefixes, memory sizes and start-up behaviour for the whole installation. It is edited rarely and reverently. If you are inheriting one of these systems, read this file first — it is the closest thing to a map of the estate that exists.

Tokenized source

Programs are stored tokenized rather than as plain text, so the file on disk is not what you typed. You list a program to see it and load text to change it. Version control, diffing and code search all need that export step — which is why so many shops version their application by copying directories with a date in the name, and why anyone who wires up a proper text export and a repository is immediately the most useful person in the building.

The utilities

File dump and rebuild, index repair, session and lock inspection, program list and compile, a console for breaking into a running program. These are the tools an administrator actually uses, and knowing them is most of what separates someone who can keep one of these systems healthy from someone who can only write code for it.

07 Who still runs it

Companies that ship physical things, mostly quietly, mostly profitably.

Distributors and wholesalers above all — fasteners and industrial supply, electrical, plumbing and HVAC, janitorial, packaging, automotive parts, welding gas, safety equipment, building materials, food service, photographic and consumer goods. Manufacturers with light assembly and a bill of materials. Rental and service businesses with a lot of small transactions. The common thread is inventory, thousands of order lines a week, and a business model that has not fundamentally changed in thirty years.

The companies are typically family-held or closely held. Big enough to need a real ERP; small enough that a nine-figure software program was never a realistic conversation. They compete on service and availability, not on software, and their systems reflect that: unglamorous, heavily customized, and extremely well fitted to the way that particular company works.

It is not rare. If you have ever received a packing slip with a monospaced, all-capitals item description on it, you have probably held the output of one of these systems in your hand.

The anatomy of one of these suites

They are more alike than their vendors would want to admit, because they were all solving the same problem. If you can find your way around one, you can find your way around most:

ModuleWhat lives thereWhat it feeds
Order entry / invoicingQuotes, orders, allocation, pick tickets, invoices, creditsInventory commitments, receivables, sales analysis, commissions
Inventory controlItem master, warehouses, costing, adjustments, physical countsEverything. This is usually the largest and most customized module
Purchasing / receivingPurchase orders, expected receipts, vendor costs, landed costInventory on order, payables, item cost
Accounts receivableCustomers, terms, credit limits, aging, cash applicationThe ledger, and the credit hold that stops an order at entry
Accounts payableVendors, invoices, approvals, check runsThe ledger, and cash forecasting
General ledgerChart of accounts, journals, periods, financial statementsThe month-end close, which is what everything else is timed around
PayrollEmployees, timekeeping, taxes, deductions, filingsThe ledger — and the most conservative code in the building
Sales analysisHistory by customer, item, salesperson, periodThe reports the owner actually reads on Monday morning

Programs are named by module and function — a short module prefix and a number, so that a program's name tells you where it lives and what kind of thing it does. Menus map one-to-one onto programs. There is a nightly batch, a month-end close, and a year-end routine that one person supervises. Somebody has a printed list taped inside a cabinet door of which jobs run in which order. All of this is common enough to be a genre.

08 Working on it today

The language is not the hard part. It never was.

Hiring

You do not hire a Business Basic developer; you hire a competent programr and give them a month. The language has perhaps fifty constructs that matter and no framework, no build system and no dependency graph. A good developer is writing useful maintenance programs within weeks.

What takes years — and what people mean when they say the knowledge is walking out of the door — is the application: which of the four hundred programs to change, what the ninety-seven fields on the order header actually do, why that flag exists, and which report the owner will notice within an hour if it moves. That knowledge is not language-specific and would be just as scarce if the system had been written in anything else.

Tooling

Expect less than you are used to and plan for it. Tokenized source means an export step before anything modern can see the code. Line numbers mean mechanical renumbering is dangerous, because branch targets are line numbers. There is no package manager, because there are no packages. The debugger is the console, a break, and print statements — which sounds bleak until you notice how small each program is and how quickly you can read one end to end.

The highest-leverage thing anyone can do in one of these shops is unglamorous: export the entire program library to text on a schedule, put it in version control, and set up a search index over it. It costs a few days and it converts "only Dave knows" into "grep".

The knowledge problem

The manual, if it exists, describes the software as it was delivered decades ago. Everything since is in the code and in one or two people's heads. This is the real risk in these installations, and it is worth being blunt about: it is not that the system will stop working. It is that the company will lose the ability to change it, and a business that cannot change its order-entry rules is a business that cannot respond to its market.

Every option in the next section should be judged first on what it does to that problem.

09 Your options

Three real choices, weighed the way you'd weigh them yourself.

There is no universally correct answer here. There is a correct answer for a specific company with a specific application, a specific bench and a specific horizon — and the fastest way to find it is to be honest about all three.

01 Stay

Keep paying, keep going.

Renew the license, refresh the hardware when it gets frightening, and keep the application maintained by whoever still knows it. This is the default, and it is chosen far more often by analysis than people admit.

What it costs
The per-seat license, every year, rising with headcount. A server refresh every several years. Whatever you spend keeping integrations glued on. Zero project cost.
What it risks
Three single points of failure, none of them technical: one machine, one person, one supplier. Recruiting gets harder every year. If the box fails without a tested restore, the recovery is measured in days.
When it's right
You have a credible horizon — a sale, a merger, a wind-down — inside a few years. Or the installation is genuinely small and the license is genuinely cheap. Do it deliberately: write down the decision, then spend a fraction of the saving on a tested restore and a documented second machine.

02 Replace

Rewrite, or buy a modern ERP.

Move the business onto packaged software or a new build, retire the application, and end the dependency completely. The clean answer, and sometimes the right one — but it is a business program, not an IT project.

What it costs
License plus an implementation multiple on top, years of your best people's attention, data migration, parallel running, and retraining everyone who touches an order.
What it risks
The rules are only written down in the code, so discovery is the project. The business keeps changing underneath the program that is re-implementing it. And the failure mode is rarely "late" — it is "abandoned in year two", with the old system still running and the budget gone.
When it's right
When the problem is the application, not the runtime. If you have outgrown the model — new channels, a different fulfilment shape, multi-entity, an acquisition strategy the software can't express — then no amount of re-hosting helps, and you should replace it and accept the cost with your eyes open.

03 Re-hostwe sell this

Change the runtime, keep the programs.

Leave the source and the data files exactly as they are and replace only the interpreter underneath them. A distribution ERP runs on our engine end to end today — sign-on, menu, inquiry, maintenance, reports. And when you move, you move entirely: we run it, we back it up, we hold the recovery plan. Nothing stays in the closet.

What it costs
An assessment, a migration, and a subscription — half the runtime license, with hosting, backups and disaster recovery included rather than itemized. Your people reach the same screens over SSH exactly as they do today, or in a browser with nothing to install; neither is a price lever. We don't publish a rate card, because the number is a ratio and not a figure — bring us the invoice you pay now and the arithmetic takes about a minute.
What it risks
Everything rests on the new runtime behaving identically to the one you have — so we treat that as a measurement problem rather than a promise. Your corpus is parsed and your files are read before anything is quoted, so anything unusual surfaces in the assessment rather than at cutover. Your system then runs in parallel with the one you have now, diffed value by value, for as long as you want it to — most people want a clean month-end close diffed before they'll pick a date. Nothing goes live on a promise.
When it's right
When the application still fits the business and the pain is the box, the license and the risk around them. Nothing is converted — your programs and your files stay in their native format, remain yours absolutely, and come back on request. The runtime you have today would still read the same bytes.

And a fourth, which combines with any of the above

Wrap it, and leave the core alone

Keep the application as the system of record and put modern surfaces around it: a web storefront, EDI, carrier and tax integrations, a reporting copy for analytics, an API in front of an interface file. Most shops are already doing some of this, usually without calling it a strategy.

It does not touch the license and it does not solve the single-server problem, so it is not an answer on its own. But it de-risks the parts your customers actually see, it can be done incrementally by people you can hire, and it is compatible with staying, replacing or re-hosting. If you only have budget for one thing this year, this is frequently the highest return per dollar.

How to work out which one you're in

Answer three questions honestly, in this order. Does the application still describe how the business works? If no, you are in option two and nothing else will help. Do you have a real exit inside three years? If yes, option one is defensible — make it deliberate. If the application fits and you're staying, then the only remaining questions are the box, the license and the bench, and those are what option three is for.

Whatever you conclude, do the unglamorous things first: prove a restore end to end, export the program library to text and put it in version control, and write down the batch schedule. Those three cost days rather than months, and every one of the options above is easier afterwards.

10 Glossary

The vocabulary, in one place.

Useful when you're reading a vendor quote, an old handover document, or a program you've just inherited.

Channel
The small integer a file is opened on. The program refers to the file by that number for as long as it is open.
Keyed file
A data file with one or more indexes maintained inside it, so records can be fetched by key rather than searched for.
Key segment
A piece of a record — an offset and a length — used to build a key. Several segments concatenated make one alternate key.
Alternate key
An additional index on the same file, letting the application read the same records in another order. Maintained by the file system on every write.
Template
A string declared with named, typed, fixed-width fields, so a record read into it can be addressed by field name.
I/O list
A named list of variables describing a record's fields, declared once at a line number and referenced by every read and write.
Mnemonic
A short code in quotes inside a print statement — clear screen, reverse video, clear to end of line — translated by the runtime for the attached terminal.
CTL
The system variable holding a code for the key that terminated the last input. How function keys are detected.
PRECISION
The number of decimal places arithmetic results are carried and rounded to. Global, and consequential.
Picture mask
The format string in a USING clause that turns a number into aligned, punctuated output.
Record lock
The exclusive hold taken when a record is read for update, released when it is written back. The unit of concurrency control.
Tokenized program
Source stored in the runtime's internal form rather than as text. You list it to read it, and load text to change it.
Device alias
A configured name standing for a printer or output destination, so programs print to a name rather than to a device path.
Spooler
The subsystem that queues, holds and releases print output, including form control and banner handling.
Configuration file
The installation-wide file defining terminals, printers, aliases, path prefixes and start-up behaviour.
Interface file
A flat file written by one system and read by another. How most integration with the outside world is actually done.
Nightly / month-end
The scheduled batch routines that recalculate totals, roll periods, and produce the reports the business runs on.
The incumbent runtime
The commercial interpreter an installation licenses today, from whichever vendor owns the dialect it was written for.

11 Questions

The ones we actually get asked.

Q1Is Business Basic the same as Visual Basic?

No. They share a distant ancestor — BASIC, created at Dartmouth in 1964 — and essentially nothing else. Visual Basic is a Microsoft language for building Windows applications. Business Basic is a family of dialects for keyed-file data processing on multi-user terminal systems, with file access, screen control and decimal arithmetic built into the language itself.

If someone tells you your ERP is "written in Visual Basic" because they saw the word BASIC, they have not looked at it.

Q2Is the language dead?

It is not being chosen for new projects, and nobody is arguing that it should be. But "dead" and "still processing your order" are different states. The applications are in production, they are still being modified, and the runtimes are still licensed and supported commercially.

A more accurate description is that it stopped growing and never stopped working. That is an unusual position for software to be in, and it is the reason this page exists.

Q3Can I run SQL against my data?

Not natively — there are no tables and no catalog, so there is nothing for a query planner to plan against. In practice there are three routes: write a program that produces an extract and load it somewhere queryable; buy a bridge from your runtime vendor if one is offered; or use a runtime that reads the keyed files directly and presents them as tables.

The important caveat with any of them is the one from the file-model section: the field layout lives in the programs, so whatever you use has to be told what the records mean, and it has to be told correctly. An export that is subtly wrong about a packed numeric field is worse than no export at all.

Q4Can we still hire people for this?

Yes, if you hire for the right thing. You are not looking for someone who already knows the language — that population is small and retiring. You are looking for a solid programr who is comfortable with small, direct, unfashionable code, and you are budgeting a month for the language and a year for the application.

The scarce asset was never the syntax. It is knowing which of the four hundred programs to change, and that would be equally scarce if the system had been written in anything else.

Q5Could we just transpile it to a modern language automatically?

Mechanically, some of it. Usefully, rarely. A translator can turn each statement into an equivalent, and what comes out the other end is several hundred thousand lines of generated code in a modern language, with the same logic, none of the idioms, and no test suite. You then own it — and you still have to verify every one of the business rules you were trying to avoid rediscovering.

The honest version of that project is a rewrite with extra steps. If the goal is to stop depending on the runtime rather than to modernize the application, replacing the interpreter underneath the existing programs is a much smaller bet.

Q6Are there date or rollover risks we should know about?

Date handling is a program-level convention, not a language feature. Different parts of the same application may store dates as six-character strings, eight-character strings, day numbers counted from an epoch, or a packed form invented by whoever wrote that module.

That means the answer is per-file and per-program, and it is discoverable: find every date field, work out its representation and its window, and write it down. Most of this work was already done once during the year-2000 audits — if that documentation still exists in a drawer somewhere, it is worth more than it looks.

Q7What happens if we stop paying the runtime license?

Generally, sessions stop starting — the application is intact, the data is intact, and nobody can sign on. Treat the renewal like a utility bill rather than a software purchase, and keep a copy of the license terms somewhere the finance team can find them without calling IT.

It is also worth knowing exactly what you are licensed for: seats or concurrent users, which machines, and what happens at renewal if your headcount has grown. Those details drive the economics of every option on this page.

Q8Who wrote this page, and can you be trusted on the options?

Unique Business Basic, part of Unique Photo — a distributor in Fairfield, New Jersey, in business since 1947. We run our own inventory, orders, purchasing and accounting on a Business Basic ERP, and we built a runtime for it because we needed one.

So we are not neutral: option three on this page is a thing we sell. What we have tried to do instead of pretending otherwise is say so plainly wherever it comes up, and write the rest of the page so it is useful even if you never speak to us. More about that below.

12 About this page

Written by people who close a month on this software.

Unique Photo has been trading since 1947. We are a distributor, and like most distributors our size we run the company on a Business Basic ERP — inventory, orders, purchasing, accounting. Every year the license invoice arrived, every year the server got older, and the rewrite quotes were absurd. So we wrote our own runtime, and today it runs our business.

5,079

Programs parsed, 100%

A real production corpus of 536,000 lines, not a benchmark suite. Every program parsed; none set aside.

1.34M

Records verified byte-exact

1,344,450 records in a customer master, read on both engines and compared record for record. Not sampled.

245/245

Arithmetic cases matching

Penny-exact decimals, including the PRECISION rounding model. This is financial software; the pennies have to match.

Disclosure

What we sell, and how we tried to keep this page honest

We sell a clean-room Business Basic runtime and the managed cloud beneath it. That is option three in the comparison above, and we've said so everywhere it comes up rather than letting the page read as neutral. Where the runtime stands today and exactly what it runs are set out on the compatibility page, in the same plain language as the rest of this.

Everything else on this page is written to be useful whether or not you ever contact us. The code samples are illustrative rather than copied from any product. The history is given in decades rather than invented dates. Where we would have been guessing, we have written around it instead. If you find something here that is wrong, we would genuinely like to know — hello@uniquebb.com.

If you run one of these

Find out what your corpus actually does.

Send a tarball of your programs and our parser runs the whole library: what parses, what doesn't, which programs are vendor-encrypted, and where the surprises are. It is automated, it costs nothing, and the report is worth reading even if you decide to stay exactly where you are.

Or just email a person: hello@uniquebb.com. If you have a question about the language rather than about our product, ask that too — we like this subject.