Skip to main content

Buenas prácticas de codificación en GeneXus

Definición de nombres

Se debe ser descriptivo con los nombres

Se intenta que el nombre sea autodescriptivo. Utilizar nomenclatura GIK para nombrar atributos. Se deben crear atributos sin el límite de los 3 caracteres si el nombre no supera los 20 caracteres y mejora la comprensión.

// mal  
Proc: CliCre

// bien
Proc: ClienteCrear
Dejo el link al SAC y debajo el contenido por las dudas.

Utilizar PascalCase al nombrar objetos, atributos y variables

// mal  
clientecrear

// bien
ClienteCrear

No utilizar underscore al inicio o final en ningún tipo de objeto, atributo o variable

// mal  
&_ClienteNombre = "John Doe"
&ClienteNombre_ = "John Doe"

// bien
&ClienteNombre = "John Doe"

Variables basadas en atributos/dominios

Para facilitar la definición y especialmente el mantenimiento, cuando sea aplicable, basar la definición en atributos/dominios.

Dominios

Nombrar los dominios enumerados sin abreviar, comenzando con el calificador del enumerado en singular y siguiendo con la entidad también en singular. Los valores enumerados también se deben especificar en singular.

// mal  
DocumentosTipo
DocumentosTipos
DocTipos

// bien
TipoDocumento{Venta,Compra,etc}
ModoDocumento{Credito, Débito}

Procedimientos

Nombrar procedimientos relacionados mediante Entidad + Atributo(depende el caso) + Complemento + Acción.
Esto permite agrupar los objetos de la misma entidad en la selección de objetos entre otros. Algunas acciones típicas son Get, Set, Load (para SDT), Insert, Update, Delete, etc. La diferencia entre Set y Update es que Set refiere a un atributo y Update a una entidad.

// mal  
CreCli
InsertarCliente
FechaCliente

// bien
ClienteInsertar
ClienteEliminar
ClienteFechaModificadoGet
ClienteFechaModificadoSet
DocumentoRecalculo

Las transacciones deben tener el nombre de la entidad en singular

// mal  
Trn:Articulos
Trn:Clientes

// bien
Trn:Cliente
Trn:Articulo

Structured Data Types

Para evitar problemas de ambigüedad, agregar prefijo SDT.

// mal  
Alertas

// bien
SDTAlerta

Identación y espaciado

Utilizar tabuladores (tab) en lugar de "espacios"

La identación ofrece una mejor lectura del código fuente.

Se deben identar las condiciones y comandos dentro de un for each

// mal  
for each
where DocumentoTipo = TipoDocumento.Venta
...
endfor

// mal
for each
defined by ClienteNombre
...
endfor

// bien
for each
where DocumentoTipo = TipoDocumento.Venta
...
endfor

Si en un for each se especifican where, defined by ú otros, dejar una línea en blanco antes del código

// mal
for each
where DocumentoTipo = TipoDocumento.Venta

if DocumentoTotal > LimiteCreacion
...
endif
endfor

// mal
for each
defined by ClienteNombre

for each Documentos
...
endfor
endfor

// bien
for each
where DocumentoTipo = TipoDocumento.Venta

if DocumentoTotal > LimiteCreacion
...
endif
endfor

// bien
for each
defined by ClienteNombre

for each Documentos
...
endfor
endfor

Dejar un espacio antes de cada parámetro

Hace a la sentencia más sencilla de leer.

// mal
parm(in:PaiId,out:&PaiNom);

// bien
parm(in:PaiId, out:&PaiNom);

// mal
&Fecha = ymdtod(2017,01,01)

// bien
&Fecha = ymdtod(2017, 01, 01)

Dominios enumerados

Evitar la utilización de textos/números fijos cuando pueden existir múltiples valores

Simplificar la lectura y no necesitar recordar el texto específico de cada opción.

// mal
if &HttpResponse = "GET"

// bien
// Crear un dominio enumerado HTTPMethods con los posibles valores ( POST, GET)
if &HttpResponse = HTTPMethods.Get

Structured Data Types

Utilizar New() en la creación de SDT.

Incluso antes de utilizar el SDT por primera vez, así queda claro que se está trabajando con un nuevo item.

// &Cliente SDT:Cliente
// &Clientes lista de SDT:Cliente

// mal
for each Cliente
&Cliente.ClienteNombre= ClienteNombre
&Clientes.Add(&Cliente.Clone())
endfor

// bien
for each Cliente
&Cliente = new()
&Cliente.ClienteNombre = ClienteNombre
&Clientes.Add(&Cliente)
endfor

Evitar crear SDT del tipo lista

Al definir la variable del item particular, se lo marca como lista.

// mal
SDT:Clientes : Lista
ClienteItem
ClienteNombre

// bien
SDT:Cliente
ClienteNombre

Strings

Utilizar format para desplegar mensajes conteniendo datos

Si la aplicación se va a traducir en diferentes lenguajes no hay que re-programar los mensajes.

// mal
&Msg = "El cliente Nro." + &ClienteId.ToString() + " se llama " + &ClienteNombre

// bien
&Msg = format("El cliente Nro. %1 se llama %2", &ClienteId.ToString(), &ClienteNombre)

Utilizar !"" para strings que no deben ser traducidos

Un traductor puede modificar constantes o códigos específicos del sistema y pueden afectar el funcionamiento, por ejemplo parámetros.

// mal
&ParVal = ParamGet("GLOBAL ENCRYPT KEY")

// bien
&ParVal = ParamGet(!"GLOBAL ENCRYPT KEY")

Para tener en cuenta: un literal sin ! en una condición where (en caso de que no pueda ser reemplazado por un dominio) afectará la manera en que se ejecutará la sentencia sql y por lo tanto impactará en el rendimiento.

 // mal
for each
where Att = "searchText"
endfor

// bien
for each
where Att = !"searchText"
endfor

Utilizar comilla doble por defecto

Estandarización de código para facilitar la lectura.

// mal
&Msg = 'Hola mundo!'

// bien
&Msg = "Hola mundo!"

Date / DateTime

Picture

Atributos/variables deben estar basada en un dominio comun y no directamente al tipo de dato Date / DateTime, esto es porque la cantidad de dígitos del año en el picture es individual y no genérico (no hay preferences que indique a nivel global la visualización). Esto incluye las variables de procedimientos, aunque no estén presentes en pantalla (ver bugs gx más abajo)

Esto facilita a la hora de elegir el picture para los date/datetime ya que se encontraría de esta manera centralizada en un dominio su definición.

  • Tener en cuenta que el objeto language maneja el formato para el idioma, pero no la cantidad de digitos para el año (por defecto son 2), es decir, la cantidad de digitos del año no es modificable desde el objeto language.

  • Tener en cuenta también los siguientes bugs de GX:

note
  • Si tengo una var date en el webform basada en el dominio con picture de 4 dígitos en el año, el date que se le asigne , de ser una variable, tiene que ser basada en el mismo dominio. En caso que sea Date y con 2 digitos muestra en el webform con dos digitos el año (no respeta el picture de la variable del form). Esto nos ocurrió con GX 16 U0.

  • Cuando se asigna &VarDate = &VarDateTime, a veces ..., el date recibe el time también. Ha pasado en varias versiones (gx15) y esto ocasiona problemas especialmente con el dialogo pseudo-conversasional de las transacciones / BC y el error "Table was changed".

Conversiones

De ser posible, No utilizar las funciones ctod,ctot,etc que dado un string en un formato determinado lo convierte a date /datetime, sino las funciones que reciben via parametro cada uno de los componentes del date/datetime (dia, mes, año, h, m,s)

&InitialDate = YMDtoD(2019, 1, 1)  y no CtoD(!"01/01/2019")

Si importamos de una planilla excel y el tipo de formato de la celda es Date, no hay problema con el formato y el multilanguage de la app y excel (al menos en GX16 U0).

 &DomDate = &ExcelDocument.Cells(&row,&Col).Date

Sino, y si podemos definir el formato, elegir formato ANSI (YYYY/MM/DD).

Comentarios

Utilizar /* ... / para comentarios multi-línea

// mal
// CrearCliente crea una nuevo cliente
// según las variables:
// &ClienteNombre
// &ClienteDirecccion

// bien
/**
* CrearCliente crea una nuevo cliente
* según las variables:
* &ClienteNombre
* &ClienteDirecccion
*/

Utilizar // para comentarios de una sola línea

Estos comentarios deben estar una línea antes del sujeto a comentar y comenzar con un espacio en blanco. Dejar además una línea en blanco antes del comentario a no ser que sea la primera línea del bloque.

// mal
&ClienteNombre = "John Doe" // Se asigna el nombre a la variable

// bien
// Se asigna el nombre a la variable
&ClienteNombre = "John Doe"

// mal
sub 'CrearCliente'
msg( "Creando cliente", status )
// Se crea el cliente
&ClienteBC = new()
&ClienteBC.ClienteNombre = "John Doe"
&ClienteBC.Save()
endsub

// bien
sub 'CrearCliente'
msg("Creando cliente", status)

// Se crea el cliente
&ClienteBC = new()
&ClienteBC.ClienteNombre = "John Doe"
&ClienteBC.Save()
endsub

Usar // ToDo: para marcar implementaciones a realizar

// ToDo: Implementar la subrutina
sub "CrearCliente"
endsub

Comandos y funciones

Utilizar minúsculas al nombrar comandos y funciones del sistema

Esto optimiza el desarrollo ya que los comandos y funciones provistas por el lenguaje se utilizan tan frecuentemente y no es necesario especificarlos en PascalCase.

 // mal
For Each
Where ClienteCodigo = &ClienteCodigo
Msg(ClienteNombre)
EndFor

// bien
for each
where ClienteCodigo = &ClienteCodigo
msg(ClienteNombre)
endfor

Utilizar cláusula where en comandos for each en lugar de usar comandos "if", siempre que se trate de atributos de la tabla extendida

Con esto logramos trasladar la condición al DBMS y hacer que forme parte de la query select evitando trabajar con grandes volúmenes de datos en el servidor de aplicación ó eventualmente en el cliente.

 // mal
for each Documento
if DocumentoTipo = TipoDocumento.Ventas
...
endif
endfor

// bien
for each
where DocumentoTipo = TipoDocumento.Ventas
...
endfor

Utilizar "when" en comandos for each para simplificar la query enviada al DBMS

 // mal
for each Documentos
where DocumentoTipo = TipoDocumento.Ventas
where DocumentoFecha >= &FchIni or null(&FechaInicio)
...
endfor

// bien
for each
where DocumentoTipo = TipoDocumento.Ventas
where DocumentoFecha >= &FechaInicio when not &FechaInicio.IsEmpty()
...
endfor

Subtipos

Para definir un Subtipo utilizar como nombre la nomenclatura: entidad origen + entidad destino + complemento.

Ejemplo: Dada la entidad Barco y País se desea definir en Barco un subtipo para País de origen y destino.

BarcoPaisOrigen 
{
BarcoPaisOrigenId*
BarcoPaisOrigenNombre
}

BarcoPaisDestino
{
BarcoPaisDestinoId*
BarcoPaisDestinoNombre
}

Tip: aprovechar automatizaciones del IDE de GX para una rápida creación de los mismos. Para ello se puede crear el subtipo y arrastrar desde la ventana del KB Explorer la transacción destino como contenido del mismo.

Definición de reglas

Cuando una transacción tiene muchas reglas, generalmente es difícil de comprender su comportamiento, requiriendo la lectura hasta el final de las mismas.

Una opción de mejora, es agrupar las mismas por comportamiento, con un comentario inicial sobre el cometido de las mismas.

Ejemplo:

// Valores por defecto
default(Campo1, Valor);
default(Campo2, Valor);

// Validaciones
error(....);

//Prompts
prompt(...);
prompt(...);

Nota: Tener en cuenta cuando se definen reglas cuales van a estar definidas bajo que tipo de bloque, si BC, WEB o ambos.

:::caution Tomado de wiki de DVelop :::