Unofficial Content
  • This documentation is valid for:

Translated from Version in Spanish

Good Programming practices in GeneXus serve to enhance understanding and clarity of the code, aside from allowing unified criteria between different programmers of the community.

Good programming practices in GeneXus based on the assumption that the code is the best documentation that may have a system, this is also the best tool of a developer to communicate their work with other programmers.

 

By using the Good programming practices in GeneXus KB code gets an added value because it acquires:

  • Easy integration and reuse
  • Easy comprehension by the programmer
  • Unification of criteria
  • Elimination of the dark zone of the code
  • Easy comunication between programmers
  • Clarity and correctness in the code
  • Increasing significance in mantaining of Software

 

(See also Design Tips for recommendations of design).


Good programming practices in GeneXus are composed of rules and recommendations.

 

Rules

 

  • Naming of attributes should use nomenclature GIK

 

  • Attributes must have description and Help

 

     Name: CliCod
     Description: Client Code
     Help: Identification of the Client in the System

 

  • Tables should have names that represent reality and not the name inherited by the transaction that creates them.
     Trn:  Cli2SisPro
     Table: Clients

 

  • The variables that refer to an attribute should be based on the same and have the same name attribute, if logic allows.

 

     Attribute: CliCod	- C(20)		- Code of Client			 
     Variable: &CliCod	- C(20)		- Code of Client

 

  • The variables that refer to an attribute should be based on the same and have the same name attribute, adding one more suffixes to qualify if necessary.(Agregado AdeL 05jul08)

 


     Attribute: CliCod - C(20) - Code of Client
     Variable: &CliCodOri - C(20) - Code of Client of Origin      <-----  Correct
     Variable: &CliCodDst - C(20) - Code of Client of Destination     <-----  Correct
     Variable: &CliDstCod - C(20) - Code of Client of Destination     <-----  Incorrecto

 

  • Definition of Subtypes

 

It is recommended that in defining these subtypes belong to a specific group and not be defined in the "None" group

Each subtype groups should take Subtype name attribute that identifies the group (primary) or a concatenation of names if multiple primary, as in the following example (Added AdeL 05jul08)

Name of Group:  BankCodOrigin

     Subtype                                 Supertype

     BankCodOrigin                           BankCod
     BankNameOrigin                          BankName


  • Report of specification

It is considered good practice for the GeneXus Analyst carefully review the report specification as it is it is the main tool for detecting errors in the code.

  • Definition of Rules


In occasion no existence of criteria within the community to define the Rules thus the code is quite "hard" to understand and difficult to search for the behavior that was scheduled for a particular attribute.

Ej:
Parm(in:EmpCod, in:&User, in:&CliCod, in:&Mode);

noaccept(CiuCod);

&CliResidueAux = udp(PcalcResidue, EmpCod, &CliCod, CliResidue);

error('Message') If Null(&User);

allownulls(EmpCod, LocCod ) ;

Call(PActInfo, EmpCod, CliCod) if <cond>;

error('Message') IF CliDir = nullvalue(CliDir ) and after(CliDir) ;

prompt(Wclients, EmpCod, CliCod);

default(CliDatCrea, Now() ) ;

noaccept(EmpCod);

Call(PInfoUsr, EmpCod,&User) if <cond>;

nocheck(EmpCod, LocCod);

msg('Residue less than zero') if CliResidue < 0;

Refcall(Wclients, EmpCod, CliCod);

Call(Pprocedure, EmpCod, CliCod) if <cond>;

default(CliArea, 'A' ) ;

 


De está manera podemos seguir el comportamiento que se programo para un atributo o variable en particular.

By observing the code we realize that we have to search for something till the end and showing no criteria to follow. There are many ways to define the rules to make them easier to understand, but we will take two criteria to be seen as good practice.

Criterion no. 1

Define the rules grouped by attribute:
if so we can continue the behavior that was scheduled for a particular attribute or variable.

 

Example:
Parm(in:EmpCod, in:&User, in:&CliCod, in:&Mode);

error('Message') If Null(&User);
Call(PInfoUsr, EmpCod, &User) if <cond>;

allownulls(EmpCod, LocCod ) ;
nocheck(EmpCod, LocCod);

Call(PActInfo, EmpCod, CliCod) if <cond>;
Call(Pprocedure, EmpCod, CliCod) if <cond>;
prompt(Wclients, EmpCod, CliCod);
Refcall(Wclients, EmpCod, CliCod);

error('Message') IF CliDir = nullvalue(CliDir ) and after(CliDir) ;

default(CliDatCrea, Now() ) ;
default(CliArea, 'A' ) ;

noaccept(EmpCod);
noaccept(CiuCod);

msg('Residue less than zero') if CliResidue < 0;
&CliResidueAux = udp(PcalcResidue, EmpCod, &CliCod, CliResidue);


Criterio Nro. 2 (Recommended)

Grouping Rules for conduct

On this way we can go directly to the sector in which depends on code is the error or to add behavior.

 

Example:
Parm(in:EmpCod, in:&User, in:&CliCod, in:&Mode);
noaccept(EmpCod);
noaccept(CiuCod);

allownulls(EmpCod, LocCod ) ;
nocheck(EmpCod, LocCod);

default(CliDatCrea, Now() ) ;
default(CliArea, 'A' ) ;

error('Message') If Null(&User);
error('Message') IF CliDir = nullvalue(CliDir ) and after(CliDir) ;
msg('Residue less than zero') if CliResidue < 0;


Call(PInfoUsr, EmpCod,&Usuario) if <cond>;
Call(PActInfo, EmpCod, CliCod) if <cond>;
Call(Pprocedure, EmpCod, CliCod) if <cond>;
&CliResidueAux = udp(PcalcResidue, EmpCod, &CliCod, CliResidue);

prompt(Wclients, EmpCod, CliCod);
Refcall(Wclients, EmpCod, CliCod);

 


/* Comment: Andrés Cuñarro
In the case of grouping rules by behavior practical results indicate we sometimes must use different rules to accomplish the task. If these (or other) sections include a style, a criterion standard can be defined for each Kb.*/

For example:

 

parm( parm1, parm2, ...);

//INSTANTIATE REGISTER (PARAMETER)

//VALUES FOR DEFAULTS
default(CliDatCrea, Now() ) ;

//INTERFACE
noaccept(EmpCod);

//FUNCTIONAL DEPENDENCIES
CliAge = Age(CliDatBirth);

//REFERENTIAL INTEGRITY
allownulls(EmpCod, LocCod ) ;
nocheck(EmpCod, LocCod);

//VALIDATION OF FIELD
error("Name of client incorrect.") if null(CliNam);

//VALIDATION OF REGISTER
error("The date of expiration can not be smaller than the document.") if DocDatExp < DocDat;

//PROMPTS
prompt(WClients, CliCod);

//ACTIONS
Call(PInfoUsr, EmpCod, &User) if <cond>;

 

Recommendations

 

  • The objects description of a KB should be clear regardless of the name.

 

     Name: ModCliDebt
     Description: Modification of Client with Debt

 

  • They must have a method to name objects. This depends greatly on the size (s) of KB (s), type of facility, inherited criteria and other number of factors, but some things to consider would be (Added by AdeL 05jul08).

     

    • At first the object, a couple of letters that identify the application (eg 'PE' for an application of Personnel, 'CO' for an application Purchasing). This could be optional, depending on the environment.
    • An identifier of the entity on which the object works (eg Customers, Invoices, Tax), which could also be mnemonic (eg Cli, Invoice, Tax)
    • One or more verbs, combined with words or abbreviations identification briefly what the object does (Compute, Delete, Edit, Create).

        

  • The description of the objects must first contain the object and then the action is executed.    
Name                        Description     
CliDebtModify               Client with Debt - Modify
InvoiceLogin                Invoice - Login
COOrderPurchaseTaxCalculate Order of Purchase - Tax - Calculate     (This would be an object of the KB Purchasing, e.g. a distributed development environment)
VEClientsWorkWith           Clients - Work with         (This would be an object of the KB Sales, e.g. a distributed development environment.)

 

 

 

  • Use mnemonic names for variables that no corresponding system attribute.

 

     To load into a variable the existence of a client.
     Correct form: ExistClient
     Incorrect form: Flag

 

  • Use mnemonic names for objects in the KB

 

     WorkWithClients

 

  • In the header of the programs you should make a frame with a short description of its purpose and useful data.

     

 

     /* 
       Author: Cristhián Gómez (urulinux@adinet.com.uy)
       Date of Creation: 26-06-2004
       Last modification:  27-06-2004
       Versión: 1.2
       Description: Cambia el estado de los movimientos luego de la autorización del Usuario
     */

 

 

 

  • Place a blank line between the definitions of events or subroutines to separate them and make programs more understandable.

     

 

 

  • Within the event you should start writing code after a tab., This facilitates the visualization of code.

 

 

     // Incorrect form:
     Event 'NewCli'
     If &CliCod = &Client
         //Code
     Endif
     EndEvent

     // Correct Form:
     Event 'NewCli'
         If &CliCod = &Client
             //Code
         Endif
     EndEvent

 

 

 

  • For the ForEach remain clear and easy to identify in events or code it's generally recommended to write as follows:

 

     // Incorrect form:
     Event 'NewCli'
     For Each
     where CliCod = &CliCod
     //Code
     EndFor
     EndEvent	

     // Correct form:
     Event 'NewCli'
         For Each
             where CliCod = &CliCod
             //Code
         EndFor
     EndEvent	

 

 

 

  • For the ForEach filters remain clearer it is recommended that a where for each condition and not using AND.

 

     //Incorrect form:
     For Each
         where CliCod = &CliCod and CliStatus = &CliStatus and CliTipo = &CliTipo
         //Code
     EndFor

     // Correct from:
     For Each
         where CliCod = &CliCod
         where CliStatus = &CliStatus
         where CliTipo = &CliTipo

        //Code
     EndFor
 

 


/* Comentario de Gabriel Icasuriaga
Otra opcion es definir toda la declaracion del for each sin indentar y recien indentar cuando comienza el codigo
aparte de eso, prefiero poner todos los atributos aunque esto sea redundante, asi de esta forma, cuando otra persona ve el codigo
la comprension es mas rapida. Tambien me gusta como quedan los "=" a la misma altura.

 

 

For Each Clicod, CliStatus
Where CliCod    = &CliCod
Where CliStatus = &CliStatus
Where CliTipo    = &CliTipo
   
         //Codigo 

 EndFor

 


/* Comentario de Nicolas Jodal: me gusta mas la version original */

/* Comentario de: Jorge Ronald Cribb - Vikam Corporation
Sugiero esta otra forma:

 

 

  • Las palabras reservadas de GENEXUS con MAYUSCULAS
  • Indentación de 4 caracteres para las opciones del FOR EACH.
  • Los conectores AND e OR indentados dentro de la opción WHERE.

 

FOR EACH Clicod, CliStatus
     WHERE CliCod    = &CliCod
         AND CliStatus = &CliStatus
         AND CliTipo    = &CliTipo
   
         //Codigo 

 ENDFOR

 


/* Comentario de Edson Geovane - Vikam Corporation
Eu só acrescentaria o uso da clausula 'defined by' após as sentenças WHERE. Acho importante porque define a tabela que será lida e ajuda ao programador identificar rapidamente esta tabela. Vamos incentivar os programadores ler os programas e entender como se fosse um idioma.

/* Comentario de: Jorge Ronald Cribb - Vikam Corporation
Para mejorar la compresión del programa sin embargo yo preferiría que el ESPECIFICADOR de Genexus escribiese al lado de cada FOR EACH (como comentario) la relación de TABLAS que está usando ese FOR EACH con toda la secuencia de JOINS e OPCIONES DE ORDER que será efectuada.
Ejemplo:

 

 

FOR EACH Clicod, CliStatus   // GX: CLIENTES
     WHERE CliCod    = &CliCod
         AND CliStatus = &CliStatus
         AND CliTipo    = &CliTipo
         //Codigo 
 ENDFOR


Nota: En este caso el comentario que dice "// GX: CLIENTES" seria colocado automaticamente por Genexus después de la especificación del objeto.
Aclarando un poco mas todavía: La información que sería colocada por Genexus en el fuente sería del mismo tipo que coloca en el informe/listado de navegación del objeto (pero un poco mas resumida).


/* Comentario de Demetrio Toledo.
Generalmente acostumbro identificar siempre a la tabla base donde voy a trabajar con el For Each utilizando la Sentencia Defined By, de la siguiente manera.

 

 

FOR EACH Clicod, CliStatus   // GX: CLIENTES
       Where CliCod    = &CliCod
       Where CliStatus = &CliStatus
       Where CliTipo    = &CliTipo
       Defined By CliEstReg
         //Codigo 
 ENDFOR


De esta manera se identifica correctamente la tabla base.
Lo cual definitivamente da la posibilidad de que cada tabla tenga por definicion un atributo que contemple la situacion del registro, en una especie de auditoria, en este caso el CliEstReg, no indicara la situacion del registro 2=MODIFICADO 4=ELIMINADO O 1=CREADO...

/* Comentario de Adilson Costa.
Aproveitando o comentário anterior onde é identificada a tabela base, além de informar o nome da tabela, identifico também a sua descrição.
Utilizo também a mesma informação no fechamento do For Each para facilitar quando estamos utilizando For Each aninhados.

 

 

For Each Order Clicod, CliStatus        // Clientes -> Tabela de Clientes
    Where CliCod    = &CliCod
    Where CliStatus = &CliStatus
    Where CliTipo    = &CliTipo

    //Codigo 

EndFor // Clientes -> Tabela de Clientes
 
/* Comentário de Fabiano Gorziza
Me parece que a boa prática aqui é, independente da forma escolhida, definir um padrão para toda a empresa.


/* Comentario de Andrés Rodríguez
Mi forma es parecida a estas últimas pero mientras genexus no describa automáticamente la tabla que se recorre, yo lo escribo manualmente para ayudar a otros que entiendan el código y para que, en el futuro al colocar cosas dentro del for each sea posible verificar que siga recorriendo lo mismo que antes. Estoy de acuerdo en poner los = a la misma altura y a indentar los where. En lo personal coloco un espacio en blanco luego de los where o defined by.

 

 

 

For Each Order Clicod, CliStatus        // TBL: CLIENTES
    Where CliCod    = &CliCod
    Where CliStatus = &CliStatus
    Where CliTipo   = &CliTipo

    //Codigo 
EndFor // TBL: CLIENTES 

 

 

 

  • Put a space after each comma (,) in the rules, call, udp, etc. to make programs easier to understand.

 

     // Incorrect form:
     parm(&CliCod,&UsuCod,&Tipo);
     call(MiObjeto,CliCod,UsuCod,&Tipo)
     // Correct form:
     parm(&CliCod, &UsuCod, &Tipo);
     call(MiObjeto, CliCod, UsuCod, &Tipo)

 

  • Avoid too short

The names of variables, subroutines, objects, etc should be as clear as possible because if someone outside work with the code he must also understand the code by generally  deciphering the names of each variable, etc.
Example: You want to load into a variable client by provider

 

   //Incorrect form:
   &CPProv

   //Correct form:
   &ClientByProvider

 

  • Clarity in the code

The clarity of the code is also considered good programming practice, in many cases programmers abuse habit of using "if" forgetting that there is "Do Case" command. Many times this is because early versions of GeneXus did not support this command and the habit is stronger than change.

 

  • Attributes must be based on Domains

We must treat all attributes are always based on a domain, so it is easier to adapt to changes in rates or longer.

 

  • Utilization of Patterns

It is recommended that all web application uses Patterns, the patterns of GeneXus offer us an ideal tool to create web applications. Facilitate migration to win a web environment and offer a practical way to solve problems we had long before. For more information read Patterns.

 

  • Avoid constant in the code

Using enumerators instead of constants in the code. That way if the constant is change it need not be changed on all sides used.
 

&Type = "CR" // BAD

&Type = BalanceType.Credit // GOOD

 

  • Maintainence of Kb´s


The nature of most projects leads us to make changes constantly on the initial knowledge we have stored in a KB. Changes in customer requirements shoot a lot of actions that sometimes makes us to change much of our business logic.

This leads to the existence of KBs with many objects, attributes and tables that are not used or are no longer used by any change or refactoring the code. This means that there is duplication or unnecessary knowledge in the KB and as a KB grows production times also grow.

There are tasks that are often made without realizing we optimized by maintaining the existing knowledge in a KB. Doing good we can lower maintenance times:

 

    • Build All

 

    • Copy Model

 

    • Update Model

 

    • Generate Help

 

    • Publication of Information with GXPublic

 

    • Backups


It would be good if we take time occasionally to delete all objects, attributes, domains, subtypes, and tables that do not use. This will improve production times and help the KB have the knowledge you need.

One of the things that makes a KB grow is to have models unused, in many cases no choice but to have several prototype and production models. A good recommendation is to  eliminate all models that are not used in a KB.

 

  • Encapsulate code using Formula Attributes


A very important aspect in maintaining the software is to centralize the definition of the various calculations performed on the data. It is recommended  to incorporate these calculations as Formula Attributes. In this way we ensure that when calculating required to perform for a given information changes, the formula attribute is changed and this is on ALL the system.

Finally a nomenclature proposal for cases where the definition of a Att formula is based on a procedure Ex: CliSdoRes = udp (P ...) Name of Procedure must match the name of the attribute. For the above example: CliSdoRes = udp (PCliSdoRes, ...), another option is to put in the procedure name the particle "Frm" in the above example: CliSdoRes = udp (PFrmCliSdoRes, ...)



 

 

 

Last update: February 2024 | © GeneXus. All rights reserved. GeneXus Powered by Globant