Ask Reuben

Genero Coding in 2024 (Reuben’s Commentary)

I can see the slides to a presentation at WWDC, what did you talk about?

At WWDC24, I gave a presentation titled “Genero Coding in 2024”.  The abstract for the presentation was …

One of our customers hired a Genero developer and commented that the coding style was reminiscent of the 90’s.  So what does Genero code look like in 2024?

Reuben will run you through some of the syntax that has been added to Genero that will make your development teams more effective and improve your Genero applications.

In this article, I’d like to expand on some of the key points in that presentation. I would suggest opening the presentation, link here. or open it from the page , and have it open in another window as you read this article.


Ship of Theseus

I didn’t include this section in every presentation.  I was never too sure how well it would travel in an English as a second language environment.  I did initially look to include it at the end of the presentation but felt it might be better at the beginning to open developers minds to the story it tried to tell.

For a long time I have been looking to take the grandfather’s axe story (or Triggers Broom as someone in the London audience pointed out) …

… this is my grandfather’s axe and has served my family for many generations, we have replaced the blade X times, and the handle Y times … is this really your grandfather’s axe?

… with an equivalent story that has many more dimensions to relate to the fact that your Genero System is made up of many facets …

  • User Interface
  • Database
  • Operating Systems
  • Interface (API’s)
  • Core Business Logic

… and that one of the benefits of Genero is that you can modify or change out one of these components and you don’t have to rewrite or replace your entire Genero application.  There are Genero customers that in the last 25 years have changed their user interface from TUI to GUI (Desktop) and now to GUI (Web), have changed from Informix to another database such as PostgreSQL and SQL Server, have changed operating systems from Linux to Windows, have changed their interfaces to other systems from flat files being passed backwards and forwards to using RESTful Web Services, and their Core Business Logic has been adapted for new business and government requirements.

I found the Ship of Theseus after noting that the Wiki page for grandfather’s axe redirects to this page on the Ship of Theseus.

To summarise the story of the Ship of Theseus ..

  • a ship took part in a historical event
  • to commemorate this historical event, every year the ship sails in a parade
  • time takes its toll on the ship and over time:
    • the ropes fray and are replaced with modern ropes
    • the sails tear and are replaced with more modern sails
    • the oars snap and are replaced with modern oars
    • the wooden hull starts to rot and some timber in the hull is replaced
    • etc.
  • some old philosophers watch the parade every year and asked each other the two following questions:
    • is the ship that sails in the parade the same ship that took part in the historical event that is being celebrated?
    • if every time a part of the ship was replaced, if this part was set aside and used to build a new ship entirely from the replaced parts, would it be more worthy for that ship to sail in the parade?

How does this relate to Genero?, if no repairs were made to the Ship of Theseus it would one day not be able to participate in the parade and one day it might sink.  If no changes are made to your Genero application, it will one day become irrelevant.  Over time you need to be prepared to enhance the code base of your Genero application so that it stays relevant.  The value proposition of Genero is that you can make those changes without having to rewrite your entire Genero application every time you want to change one of these components.


Introduction – A Conversation

A second introduction story I used for this presentation was a conversation I once had with a customer whereby the points of that conversation were …

  • hired a new developer
  • reviewed developers first piece of code
  • new developers was not using syntax and features that had been added to Genero
  • “It was like they were still coding in the 1990’s”

… afterwards I was thinking …

is it Four Js responsibility to train the pool of Genero developers, or is it a companies responsibility to train and ensure the developers they hire code in the style applicable for their organisation?

With regards to new syntax, features and best practices, we certainly have ability to …

A number of customers have a quantity of lines of code that can be measured in millions, these lines of code should be an asset, but are in some places are a liability because they have not maintained the code base over time.


Self Documenting Functions

For a number of the concepts introduced, I would show a simple snippet of code in the older style syntax, and then the same code using a newer syntax.

In this syntax I was looking at functions and how they could be more explanatory using newer syntax…


#! old.4gl
FUNCTION add(a,b)
DEFINE a,b INTEGER

-- versus --

#1 modern.4gl
FUNCTION add(a INTEGER, b INTEGER) RETURNS INTEGER
...

… there were two things to note here from syntax that was added in Genero 3.10.

  • The use of the RETURNS clause.  This allows you to inform the developers what the expected return from a function is.  This allows errors where the developer gets the number of return parameters wrong to be picked up at compile time or earlier, rather than at run-time.
  • The fully typed function parameter syntax that defines the interface to a FUNCTION in one line rather than over multiple lines. This allows the developer to see at a glance the parameters that are expected for a function they are going to call.

I would expect that in the 7 years since 3.10 was released in September 2017, that as part of maintaining your code base and keeping it modern, fresh and upto date, that your coding standard is to always use the fully-typed function parameter syntax and usage of the RETURNS clause.

A secondary point in this area was to use the TYPE syntax that was added in Genero   This was added in Genero 2.00. When passing complex data structures between two functions, you should not be defining the data structure independently in the caller and the called module.  Instead use TYPE to define the structure the same in the caller and called module.

As part of this introduction, a key concept to the ideas I will present is that if you mandate their use in your code base, how do you enforce that mandate?  I like to tell the story of how where I worked before I joined Four Js I setup a daily build and smoke test.  This ran in the early hours of the morning when the server was not being used and  would …

  • check out all source
  • recompoile all progams
  • run checks over the code to check for adherence to programming standards

… it was typically the same developers whose desk I would visit in the morning to get them to rework the code they had checked in the day before to meet our coding standard.

In those days I utlised sed, awk but they were limited in that they could only operate on a line of code, how did you check code standards that applied across multiple lines of code.  In 2024, I would utilise the same technologies that our Code Quality tool uses.  It produces an Abstract Syntax Tree (AST) and then uses XML methods to check that the resulting XML tree has the appropriate ancestors, descendants, and attribute values.

What I would have shown is how the Code Quality tool uses fglcomp -Tx … (as seen here …

gslintanalyse.4gl:133:        CALL channel.openPipe(SFMT("fglcomp -Tx %1 \"%2\"", commandLineOptions, path), "r")

… to produce this AST  I would take a small code example that compiles and execute fglcomp -Tx on this .4gl and look at the resulting output.  With Slide 13, you can hopefully see that a RETURNS clause adds a Symbol element with name=”ReturnsCl”.  If this is mising as a child of a Symbol element with name=”Function” then we know that a RETURNS clause has not been specified.

In two events, it was suggested can we add warnings to fglcomp, and have a refactor option to help adding RETURNS clauses to every FUNCTION in your code base which are sensible suggestions.


Seperate User Interface and Business Logic

I introduced this topic by putting up a small code sample and asking what is wrong with it?


INPUT BY NAME rec.*…
    AFTER FIELD fieldname
        IF rec.fieldname < 0 THEN
            ERROR “Field must be positive”
            NEXT FIELD fieldname
        END IF

… the answer is that if you are to create a Web Service API to do the same data entry the user is doing via a screen, how can the Web Service resuse this code?  It can’t, you end up with duplicated business logic, one for the program with the INPUT statement and one for the Web Service API.

The better solution is seperate the User Interface and Business Logic code to something like the following …


#! user_interface.4gl
INPUT BY NAME rec …
    AFTER FIELD fieldname
        CALL fieldname_valid(rec.fieldname) RETURNING ok, error_text
        IF NOT ok THEN
            ERROR error_text
            NEXT FIELD fieldname
        END IF

#! business_logic.4gl
FUNCTION fieldname_valid(fieldname…) RETURNS (BOOLEAN, STRING)
…
    IF fieldname < 0 THEN
        RETURN FALSE, “Must be positive”
    END IF
    RETURN TRUE, “”
END FUNCTION

… now when creating your Web Service API, the web service program can call fieldname_valid(), and you only have the business logic defined in one place.

I joined Four Js in 2008 but the company I worked for before then already had this separation of user interface and business logic in place, in fact it was there before our transformation to Genero.  The other place where we had this separation was in our form definitions.

We would not allow the usage of DEFAULT and INCLUDE in form definitions like this …


EDIT f01 = formonly.field_name, DEFAULT=“Y”, INCLUDE=(“Y”,”N”), NOT NULL;

… instead if it was expected that 100% of defaults and 100% of field validation was done via 4gl code so that it could be reused by API code.   This made working on the programs a lot simpler as their was only one place you had to look for field default values and business rules.  It also catered for the fact that business rules expressed as 4gl can be more complex than the rules defined by restricted syntax available in the .per (can you define the default value for a date as end of the current month in the .per, can your include validation rule to be 1,2 for normal users, and 1,2,3 for administrative users etc)

Even NOT NULL, you had to be careful.  If you relied on NOT NULL to force a value to be entered, you would not have that test in the 4gl, so it was a case of making sure the business logic code contained the test that a value was populated.  NOT NULL was only to be used to change the characteristics of the GUI widget ie CHECKBOX two-state or three-state.

This separation of user interface and business logic became an exercise in coding standards.  The User Interface code, and the Business Logic code could be identified by their name (one contained _ui_ …) and the coding standard became that certain syntax was only allowed in 4gl filenames that matched that standard.  For example, ERROR was only to be seen in 4gl whose filenames match *_ui_*.4gl.  DEFAULT and INCLUDE were not allowed in .per etc.

A goal when writing a Genero application is to consider the fact that with every piece of CRUD table maintenance it is feasible that not only will you want to do this CRUD table maintenance via the screen of a Genero application, you may also want to create a Web Service API to do this maintenance.  Similarly every piece of transactional data entry, not only do you want to do this via the screen of a Genero application, you may also want a Web Service API to enter the transaction.


Building Projects with IMPORT FGL

This section was intended to the main focus of this presentation.  The two previous section were a warm up to this section.

The concept is that you should no longer be linking 4gl modules together but using IMPORT FGL.

These concepts are out …

  • fgllink
  • fgl2p
  • fglrun program-name.42r

These concepts are in …

  • IMPORT FGL
  • PACKAGE
  • fglrun module-name.42m

Why?

  • because you then fit in with the rest of the world and can use tools like VS Code.
  • code completion, syntax highlighting in Code Editors
  • coding errors can be picked up at editing or compile time, rather than run time.
  • modularity of design
  • better and shorter function names

Genero 2.00 involved a change that meant you no longer required to make a runner.  Now in 2024 using Genero 5.00, you no longer need to link.

The change in your codebase is that instead of relying on a Makefle or a .4pw or some other custom script in your build environment to say what modules are linked together to produce a Genero application,  in a .4gl file, you add an IMPORT FGL statement to indicate that the symbols of this imported .4gl can be referenced inside your current 4gl

Whilst not mandatory, it is suggested that where your code references these imported symbols, that the module that is imported is explicitly specified.  This helps in the cases where a symbol is defined in two or more modules.

In applying a change across your code base you have many helpers.

  • fgllink and fglrun can use –print-missing-imports –print-imports can be used to tell you where you do not have IMPORT FGL in your existing code base
  • fglcomp –qualify-imports is a refactor option that can turn CALL bar() into CALL foo.bar()
  • fglcomp –resoive-calls can be used to produce compile time errors if a function is missing the module name.
  • PACKAGE can be uses to group many imported modules together
  • AS can be used to provide aliases and make code shorter

Note the rules on implicit compilation and note the existence of –make and –output-dir options with fglcomp.

if using Genero Studio, study the Genero and Genero (No Link) language options and study the build rules to see what commands these use.

If you have not already I would certainly suggest you investigate moving to a model where you are 100% IMPORT FGL and no longer need to use the linker to produce a 42r.  This and Responsive are the two areas I’d consider an upto date customer would be tweaking their codebase for currently.


User Interface

What we find is that Genero sites do a lot of work on the User Interface as part of their initial Genero transformation but there is little follow-up as we have added new UI features to Genero.  If you have not already moved to Universal Rendering, then there will be some review required as part of the use of Universal Rendering and I’d suggest you take the opportunity to incorporate some newer User Interface syntax

  • No TTY – I would expect most usage of TTY attributes to have been removed as part of your initial Genero transformation and to use Presentation Styles instead. If you are still TTY attributes, you will find that they get in the way of GBC Customisation and Dark Mode Themes.
  • Responsive – Incorporate Responsive features such as STRETCH=X, FLIPPED, ORIENTATION so that your screens can be used effectively in different sized devices.  I covered this in my presentation the previous day which I have outlined in a recent Ask-Reuben article.
  • UNBUFFERED – Most people see the benefit of using UNBUFFERED.  I know some were apprehensive about having in their system, some dialogs that used unbuffered, and some that did not, and so they were apprehensive about how to transform their code.  Given the number of conference attendess that said they were using UNBUFFERED, you can take confidence that that Genero transformation is possible and that it is literally a case of removing the now redundant DISPLAY statements, and reviewing any actions that should now be validate=no
  • Front-End Focus –No one was prepared to confess that they still have this coding pattern.  My message is to let the front-end naturally take care over what field has the focus.  The old Informix-4gl pattern of a BEFORE FIELD DISPLAY … ATTRIBUTES(REVERSE) .. AFTER FIELD … DISPLAY .. ATTRIBUTES(NORMAL) to better indicate what field has the focus is not necessary and should have been removed as part of your initial Genero transformation.  I feel sorry for any sites where developers still have to code that into every field in every dialog statement.
  • Single Dialogs + Single Forms – Again I was glad to see that no one was prepared to admit that they still had this pattern.  It used to concern me where there where these dialogs and forms that were duplicated save for one hidden or inactive field.  Much better to use the methods we have made available to make fields hidden or inactive.
  • Multiple Dialog – Most developers see the benefit of Multiple Dialog, the issue in moving to it was that it was not always a straight-forward modification of the existing code.
  • COMPLETER – Most developers have a coding standard of when to use COMBOBOX and when to use BUTTONEDIT.  How many added into that rule, a condition for when to use COMPLETER.  I did have an article here.  One person asked a question and I was glad they asked that question.  There is a weakness in our completer implementation in that it does not cater for a code-desc pattern like a COMBOBOX does.  See the second example here involving airport codes for a suggestion on how to get around that. What I would recommend if you do want to use COMPLETER with the code-desc pattern, let your support contact now and get yourself added to the existing enhancement request.
  • Web Components, FGLSVGCANVAS – See my articles here and here. You too can have a GUI rich interface.  If you have not already I would recommend
  • Replace StartMenu with TREE – See my article here, as I state there every-time I see a support issue with StartMenu asking for it to have additional functionality, I think well if you coded it as a TREE container and Multiple Dialog involving a DISPLAY ARRAY, you would not be asking the question.

I could’ve include a lot more, TREE, Clickable Images, FOCUSONFIELD, Collapsible Group, Aggregates, Accordion, the list goes on.  The point is to continuously review your application and the syntax we add with each release, and look to keep your application looking modern and fresh.  Your application does not have to look legacy.  The danger of looking legacy is that it will work against the retention of your application.  Perception is everything, look modern it will be seen as modern, look old and your applications head will be on the chopping block.  Even for ISV, although you don’t have the pressure to look modern in order to sell, you still have to earn the respect of your users so that they push for your applications retention.


4GL

Similar to User Interface, we have over time made additions to the 4gl language.

  • C-Extensions – Informix-4gl had no code for things like file handling, system calls, advanced maths, and so the Informix-4gl developer had to write their own C-extensions to do these activities.  Genero came supplied with libraries for these types of activities and so in your typical Genero transformation, these C-extensions were replaced by Genero library calls.  More often than not it meant that you no longer needed any C-extensions at all.  The modern day Genero developer is more likely to use nabse.Channel (file handling), os.Path (system calls), util.Math (advanced maths) than to use C-extensions.  If you do still have C-extensions, you should continually review your continued use of these, particular if they are to do things you would expect tools that write business applications should handle themselves.
  • Database Calls – there were some pockets of functionality that databases had and Informix-4gl did not and so some clever developers found that they could write code like SELECT sin(x) INTO l_sinx FROM systables WHERE tabid = 1 to do trigonometry.  If you still have clever hacks like that in your code, consider using the util.Math equivalent.
  • Genero Library – if I think back to the conversation I had with the Genero developer commenting on the new developer not using Genero new features, in the code sample I looked at it was the fact that they did not use Genero library methods that particularly caught my eye.  As a general rule, if you are iterating through a string character by character, iterating through an array row by row, you should probably ask yourself is there some Genero code that will do this?.  You will typically find the equivalent Genero library :
    • faster (executed, not interpreted)
    • memory friendly (referencing large strings by memory and not by value)
    • robust (do you trust our leap year calculation or yours!, is 2000, 2100 a leap year?)
  • Static CHAR vs STRING. It amazes me that in 2024 I still see code like …
DEFINE where_part CHAR(1000)
...
CONSTRUCT where_part ...

… when you could code …

DEFINE where_part STRING
...
CONSTRUCT where_part ...

Not only will this use less memory but it also means that you don’t have to litter your code with code to handle the case if the user somehow generates a QBE with more than 1000 characters.

Ideally your coding standard should be that any use of DEFINE … CHAR(X), you need to be able to justify the value that X takes.

  • Static ARRAY vs DYNAMIC ARRAY.  Similarly it amazes me that I sitll see code like …
DEFINE arr[1000] ARRAY OF ...

when you could code

DEFINE arr DYNAMIC ARRAY OF ...

Again a DYNAMIC ARRAY will use less memory and you don’t have to litter your code with code to handle the case if you end up with more than 1000 rows in the array.

Similarly you should be able to justify any static array with why it has that number of rows.  Good examples might be 7 for days of the week analysis or 12 for months of the year analysis.

  • DATABASE vs SCHEMA + CONENCT.  The keyword DATABASE has two meanings.  Not only does replacing it with the two distinct keywords SCHEMA and CONNECT reduce confusion, it also enables you to connect to multiple databases concurrently, something the DATABASE statement does not allow.
  • Portable SQL.  There are some coding techniques around SQL statements that if you follow will position you better in the long run
    • Joins – For Informix unique syntax such as outer and the way joins are handled, you may find that using the ANSI standard involving LEFT, RIGHT, INNER JOIN etc reduces confusion particularly for new developers coming to your environment from outside Informix
    • Date – Different databases have different formats for dates and so you should not build up SQL strings that contain an explicit date as what runs on one database may not run on another.  Get in the habit of passing any date value to the database as a parameter rather than embedding in a string
    • Serial – This is another area where databases differ and you can review your database sources to ensure that your code is portable across many databases.
    • SQL Injection – The same coding technique to pass values to the database as a parameter also protect against SQL injection.

The point is just as reviewing and using UI to  keep your application looking modern and fresh, the same can apply to your code base.  Keep reviewing it and adapting it so that your code looks modern and fresh..  If you see some code that looks long, clunky, unwieldy, repeated, chances are we have added some syntax to help you out.

For some customers what I have said in this section, this is old news and they transformed their code years ago.  For others, your code still looks like it could have been written last century.


Advanced Concepts to Reduce Code

I didn’t really have time for this section but I wanted to cover it quickly to raise awareness.  I know we have some customers who struggle to upgrade simply because of the volume of code they have to maintain, not helped in some circumstances because each of their customers has their own code base.

Ask a Genero customer, how many lines of code they are responsible for, a typical answer will be expressed in millions.  The more lines of code you have, the more difficult it is to make changes across your entire suite.  There are a number of coding techniques you can use to reduce the maintenance effort involved in making changes across your entire Genero application.

  • Code Generators such as our Business Application Modeller or 3rd party products such as FourGen, or your own custom built code generator reduce the number of lines of code you need to edit to those that are fed into the Code Generator plus the number of lines in the Code Generator itself.  If you study the Genero programs in your Application Suite, you will typically find that they fall into a number of patterns.  You may see …
    • Single Table Maintenance – a QAUD program to maintain a single database table.
    • A Master-Detail Table Maintenance – an extension of the Single Table Maintenance to maintain two tables that have a master-detail relationship
    • Transaction Data Entry – INPUT for a header, INPUT ARRAY for details, INPUT for a footer to input transacitonal data
    • Report generation, batch processing, enquiry program all start with a combination of an INPUT and CONSTRUCT

Code Generation is all about recognising these patterns and making the code base and the UX of these applications that fit each design pattern the same, so that you can quickly create another one and know that you can develop it quickly, and for the user to have the same experience.  Then when you want to change something across all the programs with the same design pattern, make the change in the generator once, regenerate all programs and you are done.

  • Pre-processor can be used to generate small snippets of code repeatedly.  There are two classic uses I have for pre-processor.
    • One is for global actions, add the same actions to every dialog.  These actions might be a help, a QBE wizard, a Copy Table etc.
    • Second is for code that is in dialog blocks.  Often the only thing different is the name of the field so rather than having 7 lines repeated over and over, just have one line with what is unique, the filename.  So on slide 70, I have the same 7 lines in every AFTER FIELD block, on slide 71 I define those 7 lines once as a macro and now every AFTER FIELD is a one line call of that macro with the current fieldname.  Adding a new field is 1 line, not 7.
  • Generic Code techniques acheive the same result but without generating code.  See fgl_zoom for a classic example of generic code that will reduce the number of lines of code in your application
  • Reflection can be added to the techniques to reduce number of lines of code.  I was stymied with my presentation as during the tour, we finally added built-in functionality to allow paste from excel in 5.00.08.  Otherwise I demonstrated how reflection could be used to interrogate an array record structure and write generic code to parse the clipboard buffer into an array variable.  i.e copy and paste from Excel.   The techniques are still relevant, I just have to come up with a different example now.

One point I didn’t cover in a slide was for ISV’s and how to reduce lines of code by not having a seperate code base for each customer.  There are techniques ISV’s can use to reduce number of lines of code they have to maintain.

These areas are my favourite topics, don’t be afraid to reach out to me if you feel you have too many lines of code.


How To Make Changes

It is easy to talk about all these code changes you can make but how do you do it with minimal disruption to your customers.  Like with us there are some things that can and only should only change in a major release.

I won’t start without the following …

  • Version Control – you need the ability to rollback, see what changes you have made.
  • Rebuild All – need the ability to check out and build all source, see what you have broken across the board.
  • Smoke Test – a test I can run quickly to ensure that programs, compiles, runs and has minimal functionality.  Not worried about the edge test, test of business logic s etc, I just want to be reassured that something fundamental has not been broken
  • QA Test – a QA team that has scripts on how to test each program and what their acceptance of it includes.  If a QA person is to fail a test, they need to be able to point to a test number or dodcument that illustrates what it is that is being tested.
  • Code Quality Test – any code changes we make to move forward we don’t want to creep back into the system.
  • Audit – ideally you know what programs you have in the system and how much they are being used.  This is when a whowhatwhen table comes in handy as well.

I like to script changes, I don’t want to type the same thing 100 times if I can help it.  With these scripts I find changes fall into some categories

  • I trust the script to make the change and I do not need to check it afterwards.
  • I will get the script to make the change but I will add a TODO into the code base at this point to manually verify the change
  • I don’t trust a script to make a change but I trust it to identify where a change should be made and insert a TODO in the code at this point.
  • I don’t trust the code to make a change or correctly identify a spot where a change needs to be made.  Insert. a TODO in the file and I will manually look at it

When I have done the work indicated by the TODO I remove the TODO.  What I also do is give a change reference number next to each TODO.  I can sum up the TODO’s to get an estimate of the work still required.

When we make code changes we need to make sure that our hard work is preserved.  Hence code standards need to be in place, adhered to, and controlled to make sure that old habits don’t make their way back into the code base.

The second to last point I made was that the less lines of code you have to maintain, the easier it is to make code changes.  Use Code Generators, Preprocessors, Generic Code to reduce the number of lines of code and apply changes quickly and consistently across your code base.  Remove unused functionality, unused programs from your code base.

Final point to finish was that a picture says a 1000 words.  Who has a print out of their database structure, their program dependency on their office wall?  Do you know that those Genero Studiuo diagrams can be printed onto multiple pages so you can make big posters?  Who has a Style Guide that contains pictures of their applications and what the UI is supposed to look like.


Summary

In this presentation I covered a lot of ground.  I don’t expect you to do everything all at once, but hopefully I have opened your eyes.  I do hope that you look after your code, treat it as an asset, sometimes that asset needs a polish, sometimes it needs some maintenance, and sometimes it can be left alone.  If I look at customers who do well, I see a user interface that looks modern and code that is well maintained and kept upto date.  If I look at customers who perhaps struggle, their User Interface looks old, and then when I look at their code it looks old as well.