Skip to content Skip to footer

Servoy Tutorial: Maintainable Code

Servoy Tutorial Photo Credit: batintherain via Compfight

This is a Servoy Tutorial on how to write maintainable code. Maintainable code means that it can be easily read, understood, extended, and maintained. This Servoy tutorial builds upon what you learned in the two other tutorials at this site, Encapsulation and Event Driven Architecture. You should study those Servoy tutorials carefully, as they discuss how to design an application with good architecture. This tutorial focuses more on the coding conventions.

As you begin to build bigger applications, or you begin working with a team of developers, it becomes imperative to establish coding conventions early in the process. The reason for this is to ensure that all code is written in in a consistent manner. Consistency ensure that the code you write today, will still be understood tomorrow, either by yourself, or any one else. This is particularly important in today’s agile development environments, where features are continually added to the base code in short weekly sprints, building upon the existing structure. Establishing coding conventions also ensures that you build a product with a consistent user-interface and experience.

The ideas presented here are not new, nor are they gospel. Feel free to adopt and change them to suit your own needs. The important part, however, is that you establish your own conventions, and start implementing them in your project and with your team.

Style Sheet

You probably know that Servoy supports the use of cascading style sheets (CSS), and that individual style classes can be assigned to your forms and form elements. This ensures that you have a consistent user-interface. More importantly, it means that you can maintain your look-and-feel from a single location. In addition, you can create multiple style-sheets and switch them at application start-up, thereby allowing your application to use themes that users could choose from.

The important thing to do here is to create a Style Guide that documents what style classes to use for what elements and in what situations, which all developers on the team can refer to. Also, create a form in your base module that demonstrates every style class you define. Just by looking at this, developers will be able to quickly identify what style class they need to use for a particular element.

maintain_1

Its also a good idea to create sample base forms, that can be cloned as starting points for new forms. This allows you to make sure the proper style class is already applied, the record navigator has been removed, the proper form parts are there, and even consistent UI elements are present. This is particularly important for table views, which may have add/delete buttons, column headers, header divider, row selector, sort, delete icons, and even a few standard methods (public and private, right?). This saves you time in development and ensures that every form looks consistent.

In this real example, you can see that the base table view includes a header, quick search tab panel, header divider, row selector, and row sort icons. There is another version that also includes addNew/deleteAll buttons in the header and a delete button for each row. By cloning from these base templates, newly created forms maintain a consistent look, functionality, and creating new forms is greatly simplified.

maintain_6

Code Formatting

Code formatting is also part of your Style Guide, it dictates how the code will be written at a high level. It is extremely important, as it will ensure that any developer will be able to work on the code, regardless of who wrote it. As mentioned in other Servoy tutorials at this site, the code will be maintained by multiple developers over its lifetime.

Indentation

So the first thing is indentation. It doesn’t matter whether you choose to use spaces or tabs, just go into the Eclipse Preferences for Javascript, and configure a Formatter profile for your project(s). Just keep tweaking this until you and your team are happy with it, then export the format template and make everyone on the team use it. There is nothing more annoying then opening up a .js file and having to format the file first before you can read it. How would you like to start your Monday morning, before your first coffee, with something like this?

//Poorly formatted code - good luck reading this
function personProto() { function privateMember(){} this.fullName = function(){ return this.first + " " + this.last; }; this.fullNameTitle = function(){ return this.fullName() + ", " + this.title; }; /** * @protected */ this.protectedMember = function(){ return "You cannot see me!"; }; }

Here is the Eclipse code Formatter:

maintain_4

Here are the Edit settings for the code Formatter:

maintain_5

And here is the same code, properly formatted. Much easier to read and work on, right? Now go ahead and enjoy your coffee.

//Properly formatted code - much easier to read
function personProto() { function privateMember(){} this.fullName = function(){ return this.first + " " + this.last; }; this.fullNameTitle = function(){ return this.fullName() + ", " + this.title; }; /** * @protected */ this.protectedMember = function(){ return "You cannot see me!"; }; }

Line Termination

One of the odd things about Javascript, is that a line of code can be terminated either by a newline, or with a semicolon. This is possible because Javascript has a mechanism known as automatic semicolon insertion (ASI). In most cases, if you leave out the semi-colon, ASI will guess correctly, and automatically insert one when it runs your code. However, it does make mistakes occasionally, resulting in difficult to locate bugs. Consider the following code, which will return an object containing some data:

function getConsultant(){
	return 
	{
		first: Gary,
		last: Dotzlaw
	}
}

The parser will see it as:

function getConsultant(){
	return;
	{
		first: Gary,
		last: Dotzlaw
	};
}

The parser will incorrectly insert the semi-colon after the return, which will cause this function to return “undefined”.

Always terminate your lines with the semi-colon, that way you control where it ends, and not leave it to ASI to guess correctly. Your just asking for trouble if you don’t, and if you used JSLint or JSHint (Eclipse addins that check for proper code style), you would be scolded to death for the bad practice.

Also, the example we just used points out another important coding convention that should be enforced; placement and alignment of enclosing braces. You should always put your opening brace on the same as the block statement, thereby mitigating the ASI problem shown previously. So do this (example also demonstrates proper alignment):

//Brace alignment should be like this
function getConsultant(){ return{ first: Gary, last: Dotzlaw }; } if (oCustomer.country === "Canada"){ // Some code }else{ // Some other code }

And not this:

//Bad brace alignment - do not do this
function getResult() { return { first: Gary, last: Dotzlaw }; } if (oCustomer.country === "Canada") { // Some code } else { // Some other code }

In case you need to break a long string over multiple lines, like an SQL query, so that it is easier to read, do it like this:

oSQL.sql = "SELECT customer_id \
		FROM customer \
		WHERE company_name = ?";

Line Length

Keep your line length short and reasonable. It’s not a contest to see who can chain the most functions together or write the longest “if” statement. No developer wants to scroll the window horizontally just to read your code. In addition, source code commits are on a line by line basis, so the shorter your lines of code, the less likely you will have a difficult merge conflict to resolve.

Instead of this:


if (oCustomer.country === "Canada" && oCustomer.state === "Manitoba" && oCustomer.city === "Winnipeg" && oCustomer.company === "Dotzlaw Consulting"){ application.output("Hello"); }

Do this:

if (oCustomer.country === "Canada" 
    && oCustomer.state === "Manitoba" 
    && oCustomer.city === "Winnipeg" 
    && oCustomer.company === "Dotzlaw Consulting"){

    application.output("Hello");
}

Blank Lines

Your code should read like logical paragraphs, and not one continuous block of code. Blank lines should be used to separate related code from unrelated code. Also, when working with the Eclipse Formatter, you can tweak it to insert blank lines after block statements, and in various other situations, to make your code easier to read. Tweak the settings with your team and establish a standard; it will make the code consistent and easier to read for everyone.

Equality

I decided to mention this, because I was recently asked why I use “===” instead of “==” in my “if” statements.

Javascript uses something called “type coercion”, which automatically will try to convert a variable to a different type in an attempt to make a particular test succeed. This can lead to some unexpected results, so you need to be sure you know what you are doing. To illustrate the point, consider the following scenarios, and then make up your mind how you want to test for equality.

application.output(123.25 == "123.25"); // true
application.output(1 == true); // true
application.output (0 == false); // true

application.output(123.25 === "123.25"); // false
application.output(1 === true); // false
application.output (0 === false); // false

I believe how you use equality should be part of your coding standard, and everyone should know what the difference is.

Variables

Everyone thinks they know how to use variables in Servoy, but I see a lot questionable practices, so its worth a short rant. You need to understand that Javascript is not like Java, or many other languages, when it comes to variables. I know we all learned in other languages to declare variables as close as possible to their first use in the code, but this is bad advice in Javascript. In Javascript, variables are hoisted to the top of the function, and will be initialized as “undefined”, regardless of where you define them. Also, in Javascript, variables declared in a function are accessible to everything in that function, including other functions (let’s ignore closures for now). Therefore, there are several problems with a typical block of code like this:

//Poor variable declaration and initialization
function myFunction(){ var iMax; var sCustomer; var bFlag; iMax = 10; for (var i = 1; i <= iMax; i++){ // some code } var rRec; // more code }

 

  • Variables should be initialized properly with the expected data type, otherwise they are initialized as “undefined”.
  • Variables should be defined at the top of the function; they will be hoisted to the top anyways.
  • The expected variable type should be specified, when it is not obvious from the initialization. Use the jsDoc @type tag for this purpose.
  • Variables should be defined together in a single statement, separated by commas, and properly aligned. Its less typing and easier to read.
  • There is no such thing as a block variable, so the “var i” declared in the for loop, will be hoisted to the top of the function and initialized as “undefined”. This variable should be defined at the top of the function, just like the other variables. Change your Eclipse template to fix this problem; it comes standard like this with Servoy.

So here is the same code with proper variable formatting and initialization.

//Proper variable declaration and initialization
function myFunction(){ var i = 0, iMax = 10, sCustomer = "", bFlag = false, /***@type{JSRecord<db:/test/t_event>} **/ rRec; for (i = 1; i <= iMax; i++){ // some code } }

I guess I should also mention, that if you listen to the gospel (Javascript: The Good Parts), you should never use “i++”. This shortcut notation made it into the Javascript language from C++, a language so bad, that it inherited its name from this bad idea. It is confusing to new Javascript developers, and other more advanced developers who come from other languages where “i++” and “++i” are available and treated differently (++i translates to “increment i, then evaluate” while i++ translates to “evaluate i, then increment“). Confusion leads to errors; excessive craftiness is what Crockford calls it.

for (i = 1; i <= iMax; i += 1){
	// some code
}

// And don't do this to increment the pointer by two, please...
i++;
i++;

// Instead, do this
i += 2;

// Same with decrement
i -= 1;

Naming Conventions

In general, use camelCase for all naming, with the exception of object constructors and enums.

Objects

 

Prefix Type Example
fs foundset fsTime
ds dataset dsTime
r record rTime
object constructor Person

Variables

Variable names should mostly be nouns. Use descriptive variable names, that shed light on what it is used for. For example, x, y and tt, are poor choices, and make your code hard to read and understand.

Prefix Type Example
b boolean bFlag
i integer iCustomerID
n float nHours
s string sFirstName
a array aReports
d date dToday
o object oContact
_n form variable _variableName

Design Elements

You do not need to name all fields and labels on a form. You need to name those that you need to access programmatically. Using the prefix will assist developers with code completion.

Prefix Type Example
btn button btnOkay
fld field fldCustomerID
lbl label lblName
tab tabpanel tabMain

Modules

You can prefix your module names to help identify the type of module.

Prefix Type Example
mod module modSales modUtils
mob mobile mobCustomer

Forms

Forms should be named with a prefix and a suffix. This will help to identify the different types of forms in the “Solution Explorer” and keep them neatly organized.

Prefix Suffix Type Example
frm _dtl detail frmCustomer_dtl
frm _tbl table frmCustomer_tbl
frm _dlg dialog frmCustomer_dlg
frm _base base form frmCustomer_base
_base developer/module base _base

Methods

Methods should begin with a verb and be followed by nouns.

Example
getPrice
createCustomer
deleteContact
calcTimeExpense

Relations

Servoy does a good job of automatically building the relation name, as you define a standard relation. Only if you are using a relation to pass a foundset (relation from one table to itself), or you are defining a global relation using a global variable or enum, should you have to edit the name.

Type Example
standard contact_to_customer
standard + global using scopes.sales.customerId contact_to_customer$sales_customerid
global using scopes.sales.customerId _to_customer$sales_customerid
foundset _to_customer

Enums

Enums are constants, and should be named using all caps. Organize your enums into a scope, like “scopes.enums”, and then organize them into logical groupings as shown below. Doing so will make it easy for developers to locate the constant they need using code completion. Enums can also be used in relations.

/**
 * @enum
 */
var CONSTANTS = {
	_0: 0,
	_1: 1,
	ON: 1,
	OFF: 0,
	ACTIVE: 1,
	INACTIVE: 0,
	_NULL: null,
	ORD: "ORD",
	EST: "EST",
	JOB: "JOB"
}

/**
 * @enum
 */
var DB = {
	SERVER: "test",
	SUPPRESS_ERRORS: true,
	MAX_RECORDS: -1
}

Scopes

With the introduction of scopes, the restriction of having all your global variables or methods in a single node has been removed. In fact, the first thing you should do when you add a new module to your solution, is delete the scope that Servoy automatically adds called “globals”. You should create your own scopes that helps you with your overall architecture, and organize information into logical groupings. For example, create scopes called “scopes.enums”, “scopes.events”, “scopes.utils”, etc. This will help keep your code organized, easier for developers to use with code completion, and easier to maintain.

Conclusion

Well, there you have it, a Servoy tutorial on how to write maintainable code. It all comes down to developing good standards and enforcing them throughout the projects life cycle. The standards will allow new developers to adjust quickly, ensure everyone’s code looks the same, and make everyone’s code easier to maintain.

That concludes this Servoy tutorial. To learn more about good architecture, best practices, and other helpful tips, please check out the other Servoy tutorials in the Related Posts area below. I hope you enjoyed this tutorial, and I look forward to bringing you more Servoy tutorials in the future.

Please subscribe to receive notification when new articles are published, and share your comments below.

Leave a comment

OUR SERVICES

AI Integration and Agent Development

We design, prototype, and deploy AI agents that streamline operations and enhance decision-making. Our expertise includes:

  • Custom AI Agent Development: Crafting autonomous systems using tools like n8n, Flowise, and PydanticAI.
  • Workflow Automation: Reducing operational inefficiencies through scalable, automated solutions.
  • Machine Learning Integration: Building predictive models to uncover actionable insights.
  • AI Adoption Consulting: Guiding businesses through the process of integrating AI technologies to achieve their strategic goals.

Expert Prompt Engineering

Effective prompts unlock the full potential of AI systems. We specialize in:

  • Custom Prompt Design: Developing precise and context-aware inputs to guide AI systems effectively.
  • Advanced Prompt Patterns: Using techniques like persona-based prompts, two-shot prompting, and reflection prompts for superior outcomes.
  • Industry-Specific Solutions: Tailoring prompts to solve challenges in fields like finance, healthcare, retail, and more.

Data Solutions and Insights

Unlock the power of your data with our comprehensive solutions:

  • Data Mining and Analysis: Extracting valuable insights to drive informed business decisions.
  • Data Visualization: Creating clear and interactive dashboards for impactful storytelling.
  • Advanced SQL Expertise: Leveraging MS SQL and PostgreSQL to build robust, scalable database solutions.

AI-Powered Project Management

Managing complex AI projects from start to finish:

  • End-to-End Project Management: From initial strategy to deployment, ensuring seamless execution.
  • Cross-Functional Collaboration: Bridging technical and business teams to align project goals.
  • Strategic Planning: Delivering AI solutions that are on time, within budget, and aligned with business objectives.

Why Choose Dotzlaw Consulting?

With over 20 years of experience in AI, software development, and workflow optimization, we combine technical expertise with business insight to deliver transformative results. Let us help you unlock the full potential of AI to drive growth and innovation.

Ready to Get Started?

Let’s collaborate to achieve your goals. Contact us today to explore how AI can transform your business.


Our Skills:

[bra_graph_container]
[bra_graph Title=’Artificial Intelligence (AI) Consulting’ Percent=’95’]
[bra_graph Title=’Prompt Engineering’ Percent=’90’]
[bra_graph Title=’Workflow Automation’ Percent=’90’]
[bra_graph Title=’Python’ Percent=’90’]
[bra_graph Title=’SQL’ Percent=’85’]
[bra_graph Title=’Data Mining and Visualization’ Percent=’80’]
[bra_graph Title=’Machine Learning’ Percent=’80’]
[bra_graph Title=’Servoy’ Percent=’99’]
[/bra_graph_container]

Core AI and Consulting Skills

[bra_list style=’arrow-list’]

  • Artificial Intelligence (AI) Consulting: Delivering transformative AI solutions tailored to business challenges.
  • Machine Learning: Expertise in designing, training, and deploying machine learning models.
  • Generative AI Workflows: Optimizing workflows with generative AI technologies for scalability and efficiency.
  • Predictive Analytics: Leveraging data to forecast trends and improve decision-making.
  • Data Mining and Analysis: Uncovering actionable insights from complex datasets.

[/bra_list]

Workflow and System Modernization

[bra_list style=’arrow-list’]

  • Workflow Automation (n8n, Flowise): Automating processes with cutting-edge tools to streamline operations.
  • Legacy System Modernization: Upgrading outdated systems using modern frameworks and AI-driven approaches.
  • AI Agent Development: Prototyping and scaling AI agents from concept to production.
  • Scalable Workflow Design: Crafting workflows optimized for efficiency and growth.
  • Business Process Optimization: Transforming processes to improve productivity and reduce costs.

[/bra_list]

Technical Expertise

[bra_list style=’arrow-list’]

  • Python, Java, JavaScript Programming: Proficiency in versatile programming languages for AI and software development.
  • Expert Servoy Programmer: Advanced expertise in Servoy development for specialized client needs.
  • AI Agent Prototyping with n8n and Flowise: Rapid prototyping using state-of-the-art AI agent development tools.
  • AI Agent Scalable Production Solutions with PydanticAI: Delivering robust, production-ready AI solutions.
  • Advanced AI Prompt Engineering: Crafting high-performing prompts for generative AI applications.

[/bra_list]

[bra_button text=’Contact Us’ url=’https://dotzlaw.com/clone/contact-me’ target=’_self’ size=’medium’ style=’rounded’ color=’tealgreen’]