Skip to content

Latest commit

 

History

History
601 lines (467 loc) · 17.5 KB

README_en.md

File metadata and controls

601 lines (467 loc) · 17.5 KB

Style Guide for GeneXus Development

by Daniel Monza

Creative Commons License

Table of Contents

  1. Naming
  2. Enumerated Domains
  3. Indent and Spacing
  4. Structured Data Types
  5. Strings
  6. Comments
  7. Commands and Functions
  8. Parameters
  9. Resources
  10. Companies that use this guide
  11. Translation
  12. Colaboradorators
  13. License
  14. Amendments

Naming

  • 1.1 Names must be descriptive

    The goal is to name stuff in a descriptive way

    // bad
    Proc: CliCre
    
    // good
    Proc: ClientCreate

  • 1.2 Use PascalCase when naming objects,attributes and variables

    // bad
    createclient
    
    // good
    ClientCreate

  • 1.3 Do not use underscore at the begining or end of any names for objects, attributes or variables.

    This could misguide a programmer more familiar with other languages that theres some privacy meaning to the name.

    // bad
    &_CliNam = "John Doe"
    &CliNam_ = "John Doe"
    Proc: _ClientCreate
    
    // good
    &ClientName = "John Doe"

  • 1.4 Enumerated domain names must start with the entity and then the type without abreviations, both in singular way. When defining a type we should be specific about the type of what.

    This facilitates the definition of attributes and variables based on the domain (GeneXus will do it automatically). Also it makes it easier to reuse the domain - i.e. it's not just a Document-Type is the visibility-type of the document and other things might have the same visibility types.

    // bad
    DocumentsTypes
    DocumentsType
    DocType
    
    // good
    DocumentVisiblityType {public,private}
    TransactionAccountType {credit, debit}

  • 1.5 Name related procedures as Entity + Attribute (depending on the case) + Action + Complement.

    This facilitates the selection of objects that deal with the same entity. Typical actions are Get, Set, Load (for an SDT), Insert, Update, Delete, etc. The difference between Set and Update is that Set refers to one attribute while Update refers to the entity

    // bad
    CliCre
    UpdateClient
    DateClient
    
    // good
    ClientUpsert
    ClientDelete
    ClientUpdateDateGet
    ClientUpdateDateSet
    ClientNameGet
    ClientNameSet
    ClientLoad
    DocumentRecalculate

  • 1.6 Use GIK nomenclature to name attributes. The use of 3 characters for the entity is recommendend when the names cannot exceed 20 characters and it has a clear and shared meaning.

    This is the standard since the beginings of GeneXus

    // bad
    CreCliDte
    DateCreationClient
    
    // good
    CliCreDte
    
    // Better
    ClientCreateDate or CliInsertDate

  • 1.7 Transactions should be named after the entity in singular.

    This faciliates working with Business Component. and it is required by some patterns GeneXus (ie.: K2BTools).

    // bad
    Trn:Clients
    Trn:Products
    
    // good
    Trn:Client
    Trn:Product

Back to Top

Indent and Spacing

  • 2.1 Use tabs (tab) instead of "spaces". This way each developer can visualize the space as they prefer since the number of characters in a tab can be configured in GeneXus preferences.

    Indent faciliates reading the source code, so if we follow a standard indentation it will make it easier for other developers to follow and understand the code.

    // bad
    if &DocumentOperationType = DocumentOperationType.Sale
    msg("Sale")
    endif
    
    // bad
    if &DocumentOperationType = DocumentOperationType.Sale
       	msg("Sale")
    endif
    
    // good
    if &DocumentOperationType = DocumentOperationType.Sale
      msg("Sale")
    endif

  • 2.2 Indent conditions in a for-each command

    // bad
    for each
    where DocumentOperationType = DocumentOperationType.Sale
    ...
    endfor
    
    // bad
    for each
    defined by ClientName
    ...
    endfor
    
    // good
    for each
      where DocumentOperationType = DocumentOperationType.Sale
      ...
    endfor

  • 2.3 If a for each specifies a WHERE condition, or a DEFINED command, leave a blank space before the next line

    // bad
    for each
       where DocumentOperationType = DocumentOperationType.Sale
       if DocumentTotalAmount > &MaxCreditAmount
          ...
       endif
    endfor
    
    // bad
    for each
       defined by ClientName
       for each Document
          ...
       endfor
    endfor
    
    // good
    for each
       where DocumentOperationType = DocumentOperationType.Sale
       
       if DocumentTotalAmount > &MaxCreditAmount
          ...
       endif
    endfor
    
    // good
    for each
       defined by ClientName
       
       for each Document
          ...
       endfor
    endfor

  • 2.4 Leave a space before each parm.

    It makes the parm sentence more readable

    // bad
    parm(in:ClientId,out:&ClientName);
    
    // good
    parm( in:ClientId, out:&ClientName);
    
    // bad
    &Date = ymdtod(2017,01,01)
    
    // good
    &Date = ymdtod( 2017, 01, 01)

Back to Top

Enumerated Domains

  • 3.1 Avoid the use of literals when there are many possible values

    It makes the code easier to read and avoids typos and confusion

    // bad
    if &HttpResponse = "GET"
    
    // good
    // Create an enumreated domain HTTPMethod with possible values ( POST, GET)
    if &HttpResponse = HTTPMethod.Get

Back to Top

Structured Data Types

  • 4.1 Use New() when crating an SDT instead of a Clone(). Also do it before the SDT is used for the first time instead of at the end (even though GeneXus supports it)

    It improves readibility

    // &ClientCollection  is aSDT:Client [List]
    // &Client is a SDT:Cliente
    
    // bad
    for each Client
       &Client.ClientName = ClientName
       &ClientCollection.Add( &Client.Clone() )
    endfor
    
    // good
    for each Client
       &Client = new()
       &Client.ClientName = ClientName
       &ClientCollection.Add( &Client )
    endfor

  • 4.1 As GeneXus allows to define a variable as a collection, avoid the creation of SDT of type collection.

    When defining the variable of the particular item, mark it as a collection

    // bad
    SDT:Clients : Collection
    	ClientItem
        	ClientNome
    
    // good
    SDT:Client
       	ClientName

Back to Top

Strings

  • 5.1 Use format to display messages that contain data

    If the application will be translated to different languages, there is no need to change the programming

    // bad
    &Msg = "The client #" + &ClientId.ToString() + " is called " + &ClientName
    
    // good
    &Msg = format( "Client # %1 is called %2", &ClientId.ToString(), &ClientName)

  • 5.2 Use !"" for strings that do not need translation

    A translator can modify constants for specific system codes that can affect the behavior (i.e. for parameters)

    // bad
    &ParmVal = ParmGet( "GLOBAL ENCRYPT KEY")
    
    // good
    &ParmVal = ParmGet( !"GLOBAL ENCRYPT KEY")

Back to Top

Comments

  • 6.1 Use /** ... */ to write multi-line comments.

    // bad
    // CreateClient creates a new client
    // parameters:
    // &ClientName
    // &ClientAddress
    sub 'CreateClient'
      // ...
    endsub
    
    // good
    /**
     * CreateClient ceates a new client
     * parameters:
     * &ClientName
     * &ClientAddress
     */
    sub 'CreateClient'
       // ...
    endsub

  • 6.2 Use // for single line comments. These comments should be in the line before the comment-target. Leave a blank line before the comment unless it is the first line of the block.

    // bad
    &ClientName = "John Doe" // Assign name to the variable
    
    // good
    // Assign name to the variable
    &ClientName = "John Doe"
    
    // bad
    sub 'CrateClient'
       msg( "Creating Client", status )
       // Create Client
       &ClientBC = new()
       &ClientBC.ClientName = "John Doe"
       &ClientBC.Save()
    endsub
    
    // good
    sub 'CreateClient'
       msg( "Creating Client", status )
    	
       // Create Client
       &ClientBC = new()
       &ClientBC.ClientName = "John Doe"
       &ClientBC.Save()
    endsub
    
    // this is also Ok
    sub 'CreateClient'
       // Create client
       &ClientBC = new()
       &ClientBC.ClientName = "John Doe"
       &ClientBC.Save()
    endsub

  • 6.3 Start comments with a space to increase readibility.

    // bad
    //Is Active
    &IsActive = true
    
    // good
    // Is Active
    &IsActive = true
    
    // bad
    /**
     *Get the Company name
     *so we can display it afterwards
     */
    &CompanyName = CompanyGetName( &CompanyId)
    
    // good
    /**
     * Get the Company name
     * so we can display it afterwards
     */
    &CompanyName = CompanyGetName( &CompanyId)

  • 6.4 Prefix comments with FIXME o TODO help other developers to understand quickly if they are looking at a possible issue that needs review. These are different from other comments as they are supposed to trigger future actions. Actions are FIXME: -- needs a solution or TODO: -- needs implementation.

  • 6.5 Use // FIXME: to highlight issues

    // FIXME: Check when &Divisor is 0
    &Total = &Number / &Divisor

  • 6.6 Use // TODO: to highlight pending implementations

    // TODO: Implement Sub
    Sub "CreateClient"
    Endsub

Back to Top

Commands and Functions

  • 7.1 Use lowercase when naming commands and system-functions

    This makes development faster as commands are used frequently and they dont need PascalCase. The only exception sometimes is Sub because it makes it more visible the start/end of the Subroutine

    // bad
    for Each
       Where ClientId = &ClientId
       Msg(ClientName)
    EndFor
    
    // good
    for each
       where ClientId = &ClientId
       msg(ClientName)
    endfor
    
    // bad
    &Date = YmdToD( 2017, 01, 01)
    
    // good
    &Date = ymdtod( 2017, 01, 01)

  • 7.2 Use do case to substitute nested if when possible. Leave a space between each case when the sentences are complex otherwise format to maximize readibility

    // bad
    if &DocumentType = DocumentOperationType.Sale
       ...
    else
       if &DocumentType = DocumentOperationTypes.Purchase
          ...
       endif
    endif
    
    // also bad
    do case
       case &DocumentType = DocumentOperationType.Sale
          do 'Something'
       case &DocumentType = DocumentOperationTypes.Purchase
         do 'Something Else'
          
    endcase
    
    // good
    do case
       case &DocumentType = DocumentOperationType.Sale
          do 'Something'
    		
       case &DocumentType = DocumentOperationTypes.Purchase
    	  do 'Something Else'
       
       otherwise
          do 'Something entirely different'
    endcase
    
    // also good - When there are many cases and the action is a one-line piece of code.
    >This facilitates read all the options without having to scroll 
    do case
       case &Action = Action.Update		do 'DoUpdate'
       case &Action = Action.Insert		do 'DoInsert'
       case &Action = Action.Regenerate	do 'DoRegenerate'
       case &Action = Action.Clean		do 'DoClean'
       case &Action = Action.Refresh	do 'DoRefresh'
       case &Action = Action.Reload		do 'DoReload'
       otherwise						do 'UnexpectedAction'
    endcase

  • 7.3 Use "where" clause in for each commands instead of "if", whenever we are dealing with extended table attributes.

    The condition is solved by the DBMS and is optimized.

    // bad
    for each Document
       if DocumentType = DocumentOperationType.Sales
          ...
       endif
    endfor
    
    // good
    for each
       where DocumentType = DocumentOperationType.Sales
       ...
    endfor

  • 7.4 Use "when" in for each commands to simplify the sentence sent to the DBMS.

    // bad
    for each Documentos
       where DocumentType = DocumentOperationType.Sales
       where DocumentDate >= &StartDate or null(&StartDate)
       ...
    endfor
    
    // good
    for each
       where DocumentType = DocumentOperationType.Sales
       where DocumentDate >= &StartDate when not &StartDate.IsEmpty()
       ...
    endfor

Back to Top

Parameters

  • 8.1 Use an SDT instead of multiple related parameters

Otherwise code might be harder to read and when parameters are modified we need to review all the callers. This might not be always possible - i.e. in webpanels

```javascript
// bad
parm( in:&ClientName, in:&ClientLastName, in:&ClientPhone, in:&ClientAddress, in:&ClientDOB)
  
// good
parm( in:&sdtClient )
```

Back to Top

Resources

Companies that use this guide

This is a list of organizations that are using this style guide. Let us know your feedback and if you want to be added to this list (or a specific fork) and we will add you.

Translation

This style guide is also available in other languages:

Back to Top

Colaboradorators

License

Creative Commons License

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License

based on AirBNB Javascript guide

Back to Top

Amendments

We encourage you to fork this guide and change the rules to fit your team's style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts.