Informix 4GLYour Informix 4GL application can have another twenty years — as readable Java you own, or running as itself on GraalVM. Prove it on your own source.Test it on your own 4GL
Está viendo la edición Perú. Está viendo la edición Colombia. You're viewing the Pakistan edition. Cambiar a la edición global →Cambiar a la edición global →Switch to the global edition →
4GL to Java · Build and package

A directory of 4GL becomes a Maven project, and a Maven project becomes one deployable artifact.

The compiler translates a program on its own when that is all you need. What a team actually deploys is a Maven project : the 4GL stays the source under src/main/4gl, the translator runs on every build, and mvn package produces one versioned jar carrying the application, its entry point and the runtime. That is what turns a directory of modules built on somebody's machine into software your delivery pipeline can ship and roll back.

A directory of 4GL becomes a Maven project and one deployable artifact: the 4GL sources stay under src/main/4gl, the translator runs inside every Maven build, and mvn package produces one versioned jar carrying the application, its entry point and the runtime.

A directory of 4GL, converted into a project an IDE can open

One command scaffolds the project. The .4gl and .per files move under src/main/4gl and stay the source ; the translator runs as a build step, so editing 4GL and pressing build is the whole workflow.

Project layoutstores/ — after j4glc project
stores/
├── pom.xml                      j4gl-maven-plugin + j4gl-runtime + shade
├── README.md
├── .gitignore                   target/
└── src/
    └── main/
        └── 4gl/                 the source — edit these
            ├── d4_main.4gl      MAIN, the ring menu
            ├── d4_cust.4gl      customer maintenance
            ├── d4_orders.4gl    order entry
            ├── d4_stock.4gl     stock enquiry
            ├── d4_report.4gl    the reports
            ├── d4_globals.4gl   GLOBALS shared across modules
            ├── custform.per     the forms, beside the code
            ├── orderform.per       that opens them
            ├── stock_sel.per
            └── helpdemo.src     the help file
Build and runthe commands
$ j4glc project stores          # scaffold, in place
  10 4GL source(s) moved into src/main/4gl

$ cd stores && mvn package     # translate, compile, package

  target/
  ├── generated-sources/j4gl/   the translated Java, a source
  │   └── fgl/*.java              root your IDE indexes
  ├── classes/
  └── stores-0.1.0-SNAPSHOT.jar   one artifact: your program,
                                  the 4GL runtime, the forms

$ java -jar target/stores-0.1.0-SNAPSHOT.jar

# Edit the 4GL, build again, and the Java is regenerated.
# Set a breakpoint in it and step through a run.

The terminal stays, and it is worth being direct about why.

This is a replacement of Informix 4GL with Java, not a move to the web. The screens, the keystrokes and the flow the business uses every day come across unchanged, and for high-volume data entry that is a genuine advantage — an operator who knows the form is faster than any mouse.

But the deeper reason is structural. 4GL was designed around the character terminal : the screen is a fixed grid of rows and columns, the dialog owns the keyboard, and the program blocks while a field is edited. Those assumptions are in the language itself, not in the runtime beneath it. A 4GL program can be rendered in a browser, and it will still be a terminal — one drawn with web technology. That is an emulation, and it is a legitimate thing to buy, but it should be recognised for what it is rather than counted as modernisation.

So we do not compete on it. What changes here is everything behind the screen : the terminal becomes what it always should have been, an input and output device in front of an ordinary Java system, and the rules that took decades to accumulate are carried across rather than re-derived. The estate keeps the interface that works and loses its dependency on a specialist hiring market. When the surface itself is the problem, that is a different exercise and it is described further down this page.

Where we do not compete : emulating a terminal in a browser.

There is an established category of product that renders 4GL character screens in a browser. It works, it is a legitimate piece of engineering, and for some estates it buys time. We do not build one, and the reason is architectural rather than competitive.

What arrives in the browser is still the terminal : 80 columns, one modal dialog at a time, tab order instead of navigation, a function-key legend at the foot of the screen. The rendering technology changes ; the interaction model does not, because the interaction model is written into the 4GL statements themselves.

It also settles nothing underneath. The estate still runs a proprietary runtime, still depends on a narrowing skills market, still cannot expose a rule as an API without a screen-scrape, and has added a rendering layer to keep in step with browser releases. The bill arrives twice — once for the emulation, and again for the modernisation it deferred.

Three layers, and a migration only moves one of them.

An Informix 4GL application is three things stacked on each other, and they are usually discussed as though they were one. At the bottom is the platform that executes it. In the middle are the rules the business actually depends on. At the top is the way those rules are put in front of a person.

A migration moves the bottom layer and leaves the middle one exactly as it was. That is the whole of the exercise — and stating it that way makes the top layer's status obvious : it is not part of the migration at all. Keep the character terminal indefinitely, because the rules do not care what is in front of them. Put a browser front end over the screens that earn one, and leave the others alone. Give the rules no interface and call them as an API, from a queue or on a schedule. Or do all three at once over one set of business logic, which is simply what a normal platform allows.

The reason to be exact about this is commercial rather than architectural. A programme that bundles the platform change and the interface redesign into one project has to justify both at once, schedule both at once and carry the risk of both at once — and it is usually the interface work that makes the estimate unacceptable. Separating them lets the continuity problem be solved on its own timetable, with the interface question still entirely open afterwards, and answered from better ground.

It sequences naturally, too : removing the runtime licence and the hiring constraint can happen while the estate runs exactly as it does today, and the surface can be taken on afterwards with the rules already reachable from whatever you choose to put in front of them. At the far end the question stops being about 4GL at all — if screens, validations and report layouts are to be described once and materialised by a platform, that is a platform decision rather than a language one, and it starts at forward path.

What a migration changes, and what it leaves open

Three layers. At the bottom the platform, which is what the migration moves: today a proprietary runtime licensed for the life of the application with a narrowing toolchain and hiring market; afterwards the JVM or GraalVM, with the connection pool, identity provider, monitoring and continuous integration the organisation already runs. In the middle the business logic, carried across unchanged either as 4GL that still compiles and runs on GraalVM or as generated Java a developer can read. At the top the interface, a decision taken later and per screen: the character terminal, a browser front end in Vue, React or Angular, no interface at all with the rules exposed as an API or a queue consumer, or all of them at once over one set of rules. Informix itself does not move.
The migration moves the bottom line. The middle line is carried across unchanged — that is the point of doing it mechanically rather than by rewriting. The top line stays open, and stays open per screen : nothing about starting closes off any of it.

The output has to survive review by someone who never wrote 4GL.

Every converter in this market emits Java that runs. The question that decides the next decade is whether a developer can open one of the files and understand it — because from cut-over onwards, that file is the specification of how the business works.

So legibility is treated as a compiler requirement rather than a courtesy. One module becomes one class ; one FUNCTION becomes one method, in source order, with its original name ; all three 4GL comment forms are carried through and re-emitted in place ; and parameter and return types are recovered by analysis instead of collapsing to Object. Nothing is merged, reordered or obfuscated, so a reviewer can hold the 4GL beside the Java and follow it line for line.

Proven against the compiler it replaces.

The question every migration turns on is whether the translation is faithful, so it is settled by measurement rather than by argument. Translated programs run against a real Informix 7.51 installation alongside the original 4GL binaries, on the same data, and the output is compared byte for byte — arithmetic, dates, NULL handling, string operations, cursors, arrays and report output alike. That is a differential test against the reference implementation, it runs on every build, and a difference is a defect.

The compiler emits Java that is meant to be read. It is not an obfuscated intermediate form, and not a runtime that interprets your 4GL behind a licence : it is code your team can review today and adopt as the source whenever they decide to — this year, in three years, or never. That option is the point of the exercise, and it is why the generated code has to be legible.

Make the application safe to change, before you change the application.

Informix 4GL was designed in the mid-1980s and matured through the 1990s, and it was very good at what it was asked to do. What did not exist then is the practice that has since become ordinary : a suite of automated tests, run on every change, by a machine. Systems written before that was normal were verified the way everything was verified then — by a person, at a terminal, working through a checklist.

What that costs an organisation today is not the testing. It is the caution. When the only way to know whether a change broke something is to have somebody exercise the screens, change becomes expensive, so less of it is attempted ; changes are batched into large releases, which makes each one riskier and the next one later ; and the people who can judge whether a change is safe are the few who remember how the system behaves. That is the arithmetic of testing by hand, not a failure of the team.

Once the application is Java, the whole of that changes at once — with tools the organisation already runs, on infrastructure it already has, and none of it built by us.

What the team gets on the first day, and keeps

These are ordinary capabilities of the Java ecosystem. That is the point : they arrive because the application is Java, so they will still be maintained in ten years by people who have never heard of 4GL.

Syntax colouring for Eclipse, IntelliJ IDEA and Visual Studio Code, generated from the compiler's own lexer rather than written by hand — so the words an editor colours are exactly the words the compiler accepts, and a test fails when the two drift.

A 4GL syntax error is reported as a marker on the statement, in the file that contains it, counted with everything else the project has to say about itself. The same feedback the rest of the organisation's code has had for fifteen years.

Maven holds what an application actually is : an entry point, linked modules, the forms that travel with it, a runtime dependency, a version and somewhere to deploy from. Every build re-translates whatever changed and produces one jar the delivery pipeline can ship and roll back.

A migrated screen runs with no terminal, driven by keystrokes queued ahead of the call, under an ordinary JUnit test in the build you already have. For most estates this is the first time the screens have been testable at all — and the tests are ordinary Java, so a developer who has never read 4GL can write them.

Breakpoints inside a screen handler, conditional breakpoints, watch expressions, hot code replace, sampling profilers and heap analysis. In 4GL the equivalent of most of this was a DISPLAY statement and a rerun.

The application builds where everything else builds. Which means a record, per release, that the pricing, credit and approval rules behaved as specified — generated by the build rather than attested by a person. Auditors are accustomed to asking for that of modern systems, and to being told it does not exist for the older ones.

The language, translated

Whole programs — functions, records, arrays, screens and reports — translate to Java, proven against the original compiler on a reference corpus. Multi-module programs link into a single Java application with an ordinary entry point — not a bundle of scripts.

Screens and reports carried across

Screen forms compile with their geometry recovered, and the interactive dialogs — menus, field input, array input, array display and query-by-example construct — are implemented. Reports carry groups, aggregates, margins, pagination and printer output.

Database access over JDBC

Cursors, transactions and host variables map onto JDBC against the same Informix instance. Column types resolve from the live schema, so a column's type is what the database says it is.

Scope is a number, not an estimate

Every construct the compiler hands to a person is marked in place and counted per program, so the work in front of you is a figure you can plan and price from the first day rather than something that emerges late. Files translate and compile around each marker, so progress is continuous and visible throughout.

Why Maven, when the compiler already translates on its own.

For a single program the compiler is enough, and it works the way a 4GL compiler has always worked : point it at a module with a MAIN, it links what that module needs and produces classes you can run. Nothing about the migration requires a build tool, and for a utility or a nightly report that is the whole story.

An application is a different object. It has an entry point, a set of modules that must be linked together, forms that have to travel with it, a runtime it depends on, a version, and somewhere to be deployed from. Those are exactly the concerns a Maven project exists to hold — so the project is not ceremony added on top of the compiler, it is the thing that turns translated modules into an application.

The plugin runs the translator as a build step, so the 4GL under src/main/4gl stays the single source while the programme is running. A developer edits 4GL, presses build, and the Java in target/generated-sources/j4gl is regenerated — registered as a source root, so it is navigable, and breakpoints in it are live.

The packaged artifact is one versioned jar : the translated program, the runtime that gives 4GL its semantics, and the compiled forms. It deploys the way every other Java artifact in the estate deploys — a repository, a pipeline, a container image, a version number that can be rolled back. For an organisation whose 4GL currently ships as binaries built on one machine by one person, that alone changes the operational risk profile.

And if you want it, there is a defined moment at which the 4GL can stop being the source : copy the generated Java into src/main/java, remove the plugin from the POM, and the project becomes an ordinary Java project maintained by hand. That step is available from the first day and obligatory on none of them — an estate can take it in year one, for a single module, or never.

The months between the decision and the cut-over.

No estate of consequence moves in a weekend. Between the day a migration is approved and the day the 4GL is switched off, there is a long middle in which both languages are real : the 4GL still runs the business and still gets changed, while the Java is reviewed, tested and learned. That period is measured in quarters, not sprints.

Most conversion tooling has nothing to say about it. It translates once and hands over a folder, which is exactly right on the last day and exactly wrong on every day before it — because the moment someone fixes a bug in the 4GL, the Java is stale and no build will tell you so.

One translator, one build

One translator, one build. On the left, 4GL sources, form definitions and the live Informix catalogue feed a single translator. The 4GL is the source throughout. The j4glc command compiles and runs a single program, or a named set, straight from the command line during development. The Maven build is how the application is built and shipped: every compile re-runs the translator over whatever changed and produces one runnable jar containing every program with its dependencies inside, indexed by the IDE for debugging. Both run the same translator against the same catalogue and produce the same Java.
The 4GL is the source throughout. The Maven build is how the application is built, versioned and shipped ; j4glc compiles and runs a single program during development. Same translator, same catalogue, same Java.

The project is a Maven project. The 4GL is its source.

Be clear about what does not change : your .4gl and .per files remain the source. The team edits 4GL, the build translates it, and the Java is what runs. Nobody is asked to learn a new language in order to get the licence and the hiring constraint off the estate.

What changes is the build. The sources sit inside an ordinary Maven project, and mvn compile is the whole workflow : every build re-runs the translator over whatever changed, compiles the result, and produces a single runnable jar with its dependencies inside it. Run it with java -jar and it lists the programs it contains ; name one and it runs.

That is what makes this a migration rather than a translation. A 4GL estate that was a directory of binaries and a shell script becomes a versioned artifact, built by the pipeline the organisation already runs, promoted through the environments everything else is promoted through, and rolled back by replacing one file. The dependency management, the test and coverage tooling, the static analysis and the continuous integration all arrive with it, because they come with Maven rather than from us.

And because the project is an ordinary Maven project, an IDE imports it and indexes the generated Java with no further setup — which is where the debugger below comes in.

For a single program, there is the direct command.

Not everything needs a project. j4glc compiles and runs one program, or a named set of them, straight from the command line — the fastest way to see what a particular module translates to, to check a change to one screen, or to run a batch program on its own during development.

It is the same translator reading the same catalogue and producing the same Java as the build does, so nothing learned from it has to be re-checked later. It is a tool for a developer at a terminal ; the Maven project is how the application is built and shipped.

Whether to stop writing 4GL is a separate question, and “never” is an answer.

Nothing about the build forces that decision or delays it. Whenever the team is ready — on their own evidence, having read and debugged the output for months — the generated Java can be adopted as the source and the translation step taken out. Equally, it need not be.

4GL is a smaller and more direct language for this work : a DECIMAL that behaves the way an accountant expects without a library, cursors and reports in the language rather than bolted onto it, a despatch rule in ten lines that Java needs a page for. A team whose domain experts write it fluently has a good reason to go on writing it, and a build that translates on every compile lets them.

What the migration ends is the runtime licence, the toolchain and the dependency on a language the market no longer teaches — on the first day, whatever you later decide about the language itself. Everything that runs is Java on a JDK from that day forward.

And the two mix on purpose. The generated Java is ordinary Java in an ordinary project, so new capability can be added in Java beside the rules — a REST endpoint over an existing rule, a scheduled job, a message consumer, a library the 4GL never had — while the rules stay in 4GL and keep being edited there. That is a working arrangement, not a transitional one, and for many estates it is the right permanent answer.

Why the Maven path is the one that builds confidence.

Both paths produce the same Java, so the argument for the Maven one is not the output — it is that a team cannot take ownership of code it has only ever read, and the Maven path keeps that code present, current and indexed at the moment a developer is curious. Reviewing twenty thousand lines of generated Java in a pull request teaches almost nothing ; setting a breakpoint inside a credit-limit check, watching a MONEY value carry its scale through an arithmetic expression, and stepping into the runtime call that reproduces a 4GL comparison teaches the whole design in an afternoon.

That is only possible if the generated Java is present, current and indexed by the development environment at the moment the developer is curious. If regenerating it is a separate command somebody has to remember, it will be out of date exactly when it matters.

The 4GL stays the source

Sources sit inside the project and are what the team edits. The generated Java is written beneath the build directory, where the conventions of the ecosystem already say it is an output rather than something to be edited — the same arrangement a parser generator or a schema binder uses.

The build keeps them in step

Every build checks whether any source is newer than the generated code and retranslates when it is. A build with nothing to do says so and does nothing. Correctness here is deliberately conservative : one changed module regenerates the program, because modules share declarations and a cleverer rule that is wrong ships stale Java.

Every file says when it was produced

Generated files carry the source they came from and the moment they were generated, and say plainly that the next build will overwrite them. Nobody discovers by accident that an afternoon of edits is about to disappear.

The database is not optional

Column types decide the shape of the generated code, so translation reads the catalogue of the database each program declares — the same requirement the original Informix compiler has. There is no cached snapshot to drift out of date, because a wrong type that looks right is worse than a build that stops.

The 4GL is edited every day of the migration, so the editor has to know it.

A migration of any size is not a stop-the-world event. The business keeps asking for changes, and for as long as the migration lasts those changes are made in 4GL — by the same people, in whatever editor they already have open. The distribution therefore carries syntax colouring for .4gl files : a TextMate grammar that serves Eclipse and Visual Studio Code, and a file type definition for IntelliJ IDEA and the other JetBrains IDEs.

Both are generated from the compiler’s own lexer rather than written by hand, which is the part that matters. A hand-maintained keyword list is a second, informal statement of what the language is : it starts slightly wrong and drifts with every change, and its errors are invisible, because a word that should be coloured and is not looks merely like a word. Generating them makes the words an editor colours exactly the words the compiler accepts, and the build fails when the grammar gains a keyword the editor files do not know.

A syntax error is reported the way a Java syntax error is : as a marker on the statement, in the file that contains it, counted alongside everything else the project has to say about itself. Not a stack trace against the build file with no line to click.

A syntax error on the line that caused it

Eclipse showing a 4GL file with syntax colouring: types, embedded SQL, statements and comments each in their own colour. An error marker sits in the ruler beside line 102, where LIKE has been mistyped as xLIKE, and the Problems view below carries the parser's message against the resource constructs.4gl at line 102, counted with the project's other errors and warnings. The Package Explorer shows the 4GL under src/main/4gl with the generated Java in target/generated-sources beside it.
The colouring and the marker in one working session. The Type column reads Maven build participant : this is the project’s real build speaking through the IDE’s own problem model, not a second tool with a second opinion. The 4GL sits under src/main/4gl, the generated Java under target/generated-sources — to the IDE and to the build, 4GL is simply another source language in the project.

What the build does when nothing has changed.

The translator runs on every build, before the Java compiler, and the first thing it does is decide whether to translate at all. That decision is the difference between a development environment that responds to a saved file and one that appears to hang on it, so it is worth being precise about. Four questions, in order — the first that answers “yes, work is needed” ends the sequence.

Has a program been deleted?

A source that is gone must take its generated Java with it, and timestamps cannot see this : removing a program leaves every surviving file untouched. Generated code for a program that no longer exists is dead code that looks live, which during a migration is the expensive kind.

Does every program have output?

Or a recorded reason for having none. The existence of an output directory is not enough : an empty one used to mean “up to date” indefinitely, over a program that had never been generated at all, and the symptom surfaced layers away as a missing entry point.

Did the editor say a 4GL file changed?

Asked only inside an IDE, which rebuilds a whole project when one file is saved. The workspace already knows what was touched, so asking it turns “translate on every save” into “translate when a 4GL file was saved”.

Is any source newer than the generated code?

The command-line equivalent, and the question that decides a build outside an IDE. Form definitions count as sources here, not only programs.

The unit of decision is the source tree, not the individual program.

When the answer is that work is needed, every program under that source directory is translated again — not only the one that changed. This is a property of the language rather than a shortcut : a 4GL program is linked from its call graph, and a module reached through GLOBALS can belong to several programs at once, so working out which programs a single edited module belongs to means parsing all of them. The analysis that would avoid the work costs the same as the work. What the build reliably avoids is translating when nothing has changed, which is the case that dominates a working day.

The catalogue is the other cost in that loop, because translation resolves declarations made against table columns. It is read once per database per build rather than once per program : a source tree of forty programs naming one database opens one connection and reads one catalogue. Database names are compared without regard to case, because Informix does not distinguish them. And a build that decides no translation is needed does not read the catalogue at all — which is what makes a no-op build independent of whether the server is reachable.

One module per subsystem, decided the day the project is scaffolded.

The build translates whatever source tree has changed, and it translates all of it : a module reached through GLOBALS can belong to several programs at once, so establishing which programs one edited module belongs to costs the same as translating them. On an estate of half a million lines and up, that arithmetic decides how the project is laid out.

So a large estate is scaffolded as several Maven modules — one per subsystem, following the boundaries the business already recognises rather than the directory structure that accumulated. Each module carries its own 4GL and translates independently, so an edit in despatch retranslates despatch and nothing else. A single source tree holding three million lines retranslates three million lines every time somebody saves a file.

It is a scaffold-time decision and an uncomfortable one to retrofit, which is why it is the first thing we work through in the setup session rather than something discovered when the build gets slow. The same partition is what later lets waves go into production independently, so it pays twice.

What an IDE gives you the moment the project is imported.

The generated folder is registered with the build as a source root, so IntelliJ IDEA, Eclipse and Visual Studio Code index it on import with no further configuration. From there the developer has the full Java toolchain — not a viewer, not a trace file, but the debugger the rest of the industry uses every day.

The tooling a 4GL developer has never had

These are ordinary Java capabilities. That is the point : the migration’s real return is that the application becomes debuggable with tools the general market already knows.

Stop inside a screen handler, step into the runtime call that implements a 4GL construct, and walk back up the stack to the statement that caused it. In 4GL the equivalent was a DISPLAY statement and a rerun.

Stop only when a customer number matches, only on the two-hundredth iteration, or the moment a specific exception is constructed anywhere in the program. Investigations that took a day of narrowing take minutes.

Inspect any record or array at any point, and evaluate an arbitrary expression against live program state without recompiling — including calling a translated function to see what it returns for the case in front of you.

Change a method body and continue the same session without restarting the application or losing the state that took ten screens to reach.

Attach the same profilers used across the Java estate. Where a report took an unexplained twenty minutes, the query and the loop responsible are now visible rather than inferred.

Unit tests, coverage, static analysis and continuous integration apply to the migrated application as they do to any other Java codebase, on infrastructure the organisation already runs.

Changing your mind is a decision, not a project.

If and when the team wants it — and that decision is theirs, taken on their own evidence — the generated Java is adopted as the project’s source and the translation step is removed from the build. Nothing has to be rewritten or re-approved to make that happen ; the code that has been running in production is the code that stays, and the jar the build produces on the day after is the jar it produced on the day before.

That is what separates a source migration from converting to another proprietary language. The translator is not a dependency you acquire. It is a scaffold you take down when it has done its work, leaving Java your team already knows how to debug — because they have been debugging it for months.

Java is a modernisation route, not the option that leaves things as they are.

This path is sometimes read as the conservative one — the estate becomes Java and otherwise stands still. That is true of the first day, deliberately, because nobody should have to fund a user-interface project in order to fund a language migration. It is not true of the second year.

What a JVM application can do that a 4GL program could not is the whole reason the language question mattered. The same rules can be exposed as an HTTP API, published to a queue, called from a scheduled job, driven by a web or mobile front end, packaged into a container, and observed by whatever the estate already uses. Integration stops being a bespoke extract written for each request and becomes ordinary work that any Java developer can pick up. A modern interface, when the business wants one, is then a project in front of a codebase that can already answer it — not a rewrite of the rules.

And none of it has to happen at once, or at all. The 4GL can go on being the source while the build translates it ; a single module can be taken over in Java while the rest stays as it is ; new work can be written in Java against rules still authored in 4GL. The routes mix, and the mixture is a normal state rather than an unfinished migration.

The screens become automatable, which they never were.

A 4GL application assumes a human : MENU waits for a keystroke, INPUT waits for a field, and nothing proceeds without a terminal. That assumption is why most estates have no automated tests of their screen programs, and no way to load data through the paths a clerk uses.

Every translated application accepts keystrokes as an argument and can run with no terminal at all. The keys are queued into the same input path a keyboard feeds, so the dialog runs exactly as it runs for a person — every field validation fires, every lookup fills, every rule that refuses a despatch still refuses it.

That turns two problems into ordinary work. Data entry can be automated through the rules rather than around them : a bulk import, an acquisition's customer file or a nightly feed goes in the way a clerk would enter it, instead of through INSERT statements that bypass every check living in the screen. And screen programs can be put under test, in a build, for most estates the first time that has been possible — it is the same mechanism the migration itself is verified with.

It is a Java API as well as a command line, so a migrated screen runs under an ordinary JUnit test : an in-memory screen, keystrokes queued ahead of the call, assertions on what appeared or on what reached the database. The test is ordinary Java — it runs in the build the organisation already has and can be written by a developer who has never read 4GL, which is usually the real constraint. Teams often start there, writing characterisation tests against the original behaviour and holding the translation to them.

The full treatment, including where it is the wrong tool, is in the paper.

Why a delivery lead should care more about this than about the translation.

The translation is the part that looks like the work. This is the part that changes what the organisation can do afterwards, and it is worth putting in the terms a steering committee weighs.

An estate with no automated tests is not badly run. It is a system built before automated testing was ordinary practice, verified the way everything was verified then. But the consequence today is a tax paid every quarter : changes are expensive because verifying them is manual, so fewer are attempted ; they are batched into large releases, which makes each release riskier and the next one later ; and the judgement of whether a change is safe sits with the few people who remember how the system behaves, so every departure takes some of it away.

Putting the screens under test moves that judgement from people to a build. A change is checked in minutes, on every commit, against the behaviour the business actually relies on. Releases get smaller and more frequent because each one is verified. A characterisation test is a written statement of what the system does in a form that fails when it stops being true, which is the documentation these estates have always been missing and the only kind that cannot go stale quietly. And because the tests are ordinary Java, a new developer can change the application with evidence rather than folklore — which is what makes them productive on the legacy system in their first month rather than their first year.

In regulated sectors there is usually a further argument, and it is often the one that carries : an automated suite produces evidence per release that the rules behaved as specified, generated by the build rather than attested by a person.

None of it requires the 4GL to be abandoned. Tests written this way exercise the rules wherever they live — in 4GL the build translates, in Java the team has adopted, or both at once — so the discipline can be established first and the language decisions taken later, on their own merits. For most estates that is the right order : make the system safe to change, then decide what to change.

The paper.

The engineering behind the compiler is written up in full — the constraint that shapes every design decision, the pipeline and its intermediate representation, how an untyped 4GL signature becomes a typed Java method, why decimal arithmetic is what a naive translation loses quietly, a verbatim credit-limit rule and its translation, the terminal surface reproduced on the JVM, what the build produces, and how fidelity is proven against the original binaries rather than asserted.

4GL to Java — the compiler, in full (PDF) — written for the architect who has to decide what happens to the estate.

For the CIO

The alternatives, and what this touches.

How this differs from the other routes in the market, and what it leaves alone in the estate.

How is this different from other 4GL-to-Java converters?

Chiefly in what the Java looks like on the day you inherit it. Automated conversion is not hard to demonstrate ; producing Java that a developer can read, review and maintain for a decade is the part that decides whether the investment holds. So structure is preserved rather than flattened — one module to one class, one function to one method in source order, comments carried through, types recovered by analysis instead of collapsing to Object. The second difference is how fidelity is established : translated programs are run against your original 4GL binaries on the same data and compared byte for byte, rather than summarised as an automation percentage.

Does the database change?

No. This migrates the application source, not the data tier. The schema, the data and the Informix instance stay exactly as they are, and the migrated Java reaches them over JDBC. Changing one variable at a time is what makes the result auditable.

For the CTO and the architecture team

Proven, on your source.

How fidelity is established, and how the answer for your own estate is produced before anything is committed.

How do we know the translated program behaves as the original?

By running both. The same programs are compiled by the original Informix toolchain and by this compiler, executed against the same database, and the output is compared byte for byte — arithmetic, dates, NULL handling, cursors, screens and reports alike. That differential test runs against a real Informix installation on every build of the compiler, and a difference is a defect. Fidelity is treated as an engineering measurement, not a reassurance — because it is the principal risk of any migration, ours included, and the only honest way to retire it is to measure it.

Will it handle our code?

That question has an exact answer, and it is produced rather than promised. An assessment translates a representative slice of your own estate and returns the evidence : what translated, compiled and ran ; the output compared against your own 4GL binaries ; and every construct that needs a human decision, marked in place and counted per program. Whole applications — screens, dialogs, reports and database access — translate and run, and the assessment turns that general fact into the specific figure for your code, which is the only figure worth planning a programme from.

What does the team work with from minute one?

An ordinary Maven project, scaffolded around the existing 4GL : the sources stay the sources, the translator runs on every build, and the result is one versioned artifact your pipeline can ship. And JUnit, from the same first build — the runtime provides the machinery to drive a 4GL program with no terminal, keystrokes queued ahead of the run and assertions on what it displayed, so screen programs go under automated test for the first time. Characterisation tests can be written before cut-over, by Java developers who have never read a line of 4GL — which means the estate becomes safe to change before anything about it changes.

For the 4GL team

Your knowledge becomes more valuable, not less.

The people who know the estate best are the ones a migration cannot succeed without — and this one is built around that fact.

Is this the end of our 4GL work?

No — and the architecture is the proof, not a reassurance. The 4GL stays the source for as long as that suits the people who know the rules : you keep authoring 4GL, the build translates it on every compile, and nothing obliges anyone to stop. The constructs the compiler deliberately escalates for a human decision are decided by the people who understand what the code means — which is you. Nobody else knows which screen is used daily, which edge case is load-bearing, or why the surcharge exemption works the way it does.

What changes for us day to day?

The toolchain around you gets dramatically better while the language stays the same : an editor that colours 4GL correctly, errors on the line that caused them, a build that produces one versioned artifact, and screens under automated test for the first time. Java developers can join the estate without displacing anyone — they write tests and new capability around rules you continue to own in the language you know best.

For the delivery lead

Fitting it to your pipeline.

What the build needs, what it produces, and what a team has to have in place before starting.

Do we have to adopt Maven?

In practice it is what you want, because it is what brings the rest with it — the runnable jar, dependency management, IDE integration, the test and coverage tooling and the pipeline. Scaffolding the project around existing 4GL is a single command and the sources stay where they are. For a developer who simply wants to see one module translated, or to run one batch program during development, j4glc does that from the command line without a project at all.

How does the application actually get deployed?

As a single runnable jar containing every program in the estate, with its dependencies inside it — so there is no classpath to assemble and no shell script wrapping a directory of binaries. Run it with no arguments and it lists the programs it contains ; name one and it runs. That is an ordinary Java build output, so it versions, promotes and deploys through whatever pipeline the organisation already uses for its other services, and rolling back is replacing one file.

Does this mean the build now depends on a database being reachable?

Yes, for any program that declares one, and this matches the behaviour of the original Informix compiler. Column precision and scale determine the generated types, so a build without the catalogue would be guessing. Programs that declare no database translate with no server at all.

What does a team need to have in place?

A JDK, a Maven build and network access to the Informix instance from wherever the build runs. Nothing else — no agent, no runtime licence and no vendor infrastructure.

For the developer

The daily loop.

What you edit, what stops you editing the wrong thing, and what the debugger gives you.

Does j4glc produce different Java from the Maven build?

No. It is the same translator reading the same catalogue and producing the same output ; the build simply wraps it in a project and packages the result. That equivalence is verified rather than assumed — the same program is translated both ways and the results are compared.

What stops a developer editing the generated Java by mistake?

Its location and its header. The code sits beneath the build directory, which the ecosystem already treats as output and which source control ignores by default, and every file states that the next build overwrites it. When editing it by hand becomes the right thing to do, that is the cut-over, and it is made deliberately.

Do the terminal screens work under the debugger?

Yes. Screens defined in the original form files are translated alongside the program, so a form opens under a debugger exactly as it does in production and can be stopped on mid-entry. The application is an ordinary Java process throughout.

Open it in your own IDE.

An assessment returns a slice of your estate as a project your developers build, run and step through on their own machines. A morning with a debugger settles more than any figure we could quote.