How do I use an AI-powered IDE in OutSystems
António Carvalho
The industry has evolved a lot in the last year regarding AI capabilities when creating applications, and Cursor is becoming a standard tool for AI-assisted development.
Let’s see how we can connect OutSystems and Cursor to take advantage of their combined strengths.
By now, everyone has heard the term vibe-coding:
A software development approach that uses AI to generate code from natural language descriptions, allowing developers to focus on the overall concept and desired outcomes rather than the intricate details of coding.
And being a lazy programmer, I want to take advantage of everything that makes my job easier and faster. 😆
How do I use it
Since my initial days working with OutSystems, part of my focus was on how to improve Developer Experience and how to make my working hours more enjoyable and consequently more productive.
Removing the typing of all CSS and Javascript from inside OutSystems and avoid hours of publish & refresh dependencies are an essential part of that. I can build complex components for hours… without touching the OutSystems Editor and develop everything on my preferred IDE.
After typing the CSS and Javascript…
I click save…
The bundler works… it takes +/- 100ms…
Refresh the browser… (mandatory for JS, not for CSS)
Interact with the screen
I can take advantage of proper code formatting, intellisense, Typescript and Sass. Usually my codebase is composed with hundreds of files, bundled in one or two single files (CSS+JS).
This generated/bundled file, which will later be inserted into an OutSystems application and deployed, initiating its regular SDLC.
But all the development process which can take hours or days, was done previously with modern techniques.
If you want to know more about this topic, take a look at this video:
https://youtu.be/zK5iWqZvJdU?si=9rpCarLQP7qn37na 😆
Meet Cursor!
Cursor is an Integrated Development Environment (IDE) forked from Visual Studio Code. It integrates advanced AI features such as:
AI-Assisted Coding;
Natural Language Editing;
Intelligent Code Completion;
AI Chat Panel;
Agent Mode;
Cursor doesn’t ship with its own proprietary models — it’s an IDE that can connect to various large-language-model back-ends.
Cursor models
We can talk with it or ask specific questions about our code since it has access to our entire codebase, analyses dependencies and understands some context.
AI prompt interfaces
But this is not an article about Cursor and its AI features, there are plenty of videos and information about it.
I want to address how to use them in OutSystems. 🤓
Here’s my workflow
Let's imagine I want to create a component called MyThing.
The Javascript
On my Javascript code, inside Cursor, I will create the necessary component class:
class MyThing {
#property1;
#property2;
constructor(obj_in) {
// some code here
// obj_in will be the initial input parameters
this.methodA();
}
methodA() {
// do something here
}
methodB() {
// do something here
}
parametersChanged(obj_in) {
// do something when input parameters change
// obj_in will come from the Block OnParametersChanged event
}
destroy() {
// the cleanup, most common is the removal of listeners
}
}
OutSystems Editor
It does not matter if it's O11 or ODC, this applies to both.
On my Library, I create a Block, with:
any number of input parameters of type Text, Integer…
a local variable called Instance of type Object;
event OnReady;
event OnParametersChanged;
event OnDestroy;
The Block
OnReady
A Javascript node with n input parameters and one output parameter Instance (type object);
Assign this output parameter to the Instance Block local variable;
// node new
$parameters.Instance = new MyThing({
parameter1: $parameters.parameter1,
parameter2: $parameters.parameter2
});
OnReady handler
From now on, this Instance will hold a permanent reference to the class instance created on the Block initialization.
OnParametersChanged
We simply have a Javascript node to receive the input parameters and pass them on as arguments.
Get António Carvalho’s stories in your inbox
Join Medium for free to get updates from this writer.
This object, passed as an argument, will be received on the parametersChanged method, as obj_in.
// node parametersChanged
$parameters.Instance.parametersChanged({
parameter1: $parameters.parameter1,
parameter2: $parameters.parameter2
});
OnParametersChanged handler
OnDestroy
This will have a very simple Javascript node.
$parameters.Instance.destroy();
Done. This is it.
create the Javascript class code;
create the Block that initiates an instance of said class;
multiply this for all the components in your codebase;
the Instance local variable on each Block, is the crucial link piece between high-code and low-code; 👈
From this point onwards, the OutSystems side will not change (or will rarely change), and the Javascript code can evolve for years and scale to whatever requirements are needed.
In fact, I can alter the behaviors and interactions of my component without even opening the Block. I just need to update the Javascript and whatever is executed after the new call (instance creation).
Lets pause. I will say this again slowly:
👉 I have Blocks, with several input parameters and events, created years ago, that can evolve today without Breaking Changes.
👉 All all the code is versioned on Git! We have branching and proper tracking history.
Important note: I make all my components as dummy and agnostic as possible. They only receive input parameters and communicate by events and never store any data. The source of truth is always the Model inside the Reactive Web App context. 👆
Additional features
DOM and Events
the Block can have any containers, text, expressions we desire, with interaction handlers (eg: click) added inside ServiceStudio;
we can add containers, text, expressions inside the Block (without events) and then add the event interactions only on the high-code;
the Block can be an empty shell, and we can generate DOM dynamically inside the Block with the appropriate event listeners;
Both ways
I can access the internal Model from the Reactive Web App from my high-code, by invoking Block client actions;
from my Javascript high-code, I can trigger Block events and implement communication to its parents;
Bundling and pre-processor
we can have our codebase split in multiple separate files with a proper tree folder structure, instead of one big monolithic file;
we can use Typescript with all its advantages;
we can embed the necessary CSS directly in the Javascript;
Namespacing
we can organize and group related code under a single and unique object;
we are not polluting the global window, and everything is inside a controlled namespace;
// create a global object
const MyAwesomeLibrary = {};
// and the components creation...
MyAwesomeLibrary.MyThing = class MyThing {
#property1;
#property2;
// ...
};
// usage on the OnReady
$parameters.Instance = new MyAwesomeLibrary.MyThing({
parameter1: $parameters.parameter1,
parameter2: $parameters.parameter2
});
Ufff… we have covered so much. 😮💨
But now we are ready for the good stuff, and we can start prompting our brains out!
Conclusion
Like I said at the beginning of the article, the industry has evolved a lot in the last year regarding AI capabilities.
At the same time, I believe proper Frontend Architecture will always be necessary when working with enterprise scalable applications.
It is possible to mix them and create better applications: OutSystems + Frontend Engineering + AI coding.
I hope this article will help you take advantage of that.
António Carvalho
The industry has evolved a lot in the last year regarding AI capabilities when creating applications, and Cursor is becoming a standard tool for AI-assisted development.
Let’s see how we can connect OutSystems and Cursor to take advantage of their combined strengths.
By now, everyone has heard the term vibe-coding:
A software development approach that uses AI to generate code from natural language descriptions, allowing developers to focus on the overall concept and desired outcomes rather than the intricate details of coding.
And being a lazy programmer, I want to take advantage of everything that makes my job easier and faster. 😆
How do I use it
Since my initial days working with OutSystems, part of my focus was on how to improve Developer Experience and how to make my working hours more enjoyable and consequently more productive.
Removing the typing of all CSS and Javascript from inside OutSystems and avoid hours of publish & refresh dependencies are an essential part of that. I can build complex components for hours… without touching the OutSystems Editor and develop everything on my preferred IDE.
After typing the CSS and Javascript…
I click save…
The bundler works… it takes +/- 100ms…
Refresh the browser… (mandatory for JS, not for CSS)
Interact with the screen
I can take advantage of proper code formatting, intellisense, Typescript and Sass. Usually my codebase is composed with hundreds of files, bundled in one or two single files (CSS+JS).
This generated/bundled file, which will later be inserted into an OutSystems application and deployed, initiating its regular SDLC.
But all the development process which can take hours or days, was done previously with modern techniques.
If you want to know more about this topic, take a look at this video:
https://youtu.be/zK5iWqZvJdU?si=9rpCarLQP7qn37na 😆
Meet Cursor!
Cursor is an Integrated Development Environment (IDE) forked from Visual Studio Code. It integrates advanced AI features such as:
AI-Assisted Coding;
Natural Language Editing;
Intelligent Code Completion;
AI Chat Panel;
Agent Mode;
Cursor doesn’t ship with its own proprietary models — it’s an IDE that can connect to various large-language-model back-ends.
Cursor models
We can talk with it or ask specific questions about our code since it has access to our entire codebase, analyses dependencies and understands some context.
AI prompt interfaces
But this is not an article about Cursor and its AI features, there are plenty of videos and information about it.
I want to address how to use them in OutSystems. 🤓
Here’s my workflow
Let's imagine I want to create a component called MyThing.
The Javascript
On my Javascript code, inside Cursor, I will create the necessary component class:
class MyThing {
#property1;
#property2;
constructor(obj_in) {
// some code here
// obj_in will be the initial input parameters
this.methodA();
}
methodA() {
// do something here
}
methodB() {
// do something here
}
parametersChanged(obj_in) {
// do something when input parameters change
// obj_in will come from the Block OnParametersChanged event
}
destroy() {
// the cleanup, most common is the removal of listeners
}
}
OutSystems Editor
It does not matter if it's O11 or ODC, this applies to both.
On my Library, I create a Block, with:
any number of input parameters of type Text, Integer…
a local variable called Instance of type Object;
event OnReady;
event OnParametersChanged;
event OnDestroy;
The Block
OnReady
A Javascript node with n input parameters and one output parameter Instance (type object);
Assign this output parameter to the Instance Block local variable;
// node new
$parameters.Instance = new MyThing({
parameter1: $parameters.parameter1,
parameter2: $parameters.parameter2
});
OnReady handler
From now on, this Instance will hold a permanent reference to the class instance created on the Block initialization.
OnParametersChanged
We simply have a Javascript node to receive the input parameters and pass them on as arguments.
Get António Carvalho’s stories in your inbox
Join Medium for free to get updates from this writer.
This object, passed as an argument, will be received on the parametersChanged method, as obj_in.
// node parametersChanged
$parameters.Instance.parametersChanged({
parameter1: $parameters.parameter1,
parameter2: $parameters.parameter2
});
OnParametersChanged handler
OnDestroy
This will have a very simple Javascript node.
$parameters.Instance.destroy();
Done. This is it.
create the Javascript class code;
create the Block that initiates an instance of said class;
multiply this for all the components in your codebase;
the Instance local variable on each Block, is the crucial link piece between high-code and low-code; 👈
From this point onwards, the OutSystems side will not change (or will rarely change), and the Javascript code can evolve for years and scale to whatever requirements are needed.
In fact, I can alter the behaviors and interactions of my component without even opening the Block. I just need to update the Javascript and whatever is executed after the new call (instance creation).
Lets pause. I will say this again slowly:
👉 I have Blocks, with several input parameters and events, created years ago, that can evolve today without Breaking Changes.
👉 All all the code is versioned on Git! We have branching and proper tracking history.
Important note: I make all my components as dummy and agnostic as possible. They only receive input parameters and communicate by events and never store any data. The source of truth is always the Model inside the Reactive Web App context. 👆
Additional features
DOM and Events
the Block can have any containers, text, expressions we desire, with interaction handlers (eg: click) added inside ServiceStudio;
we can add containers, text, expressions inside the Block (without events) and then add the event interactions only on the high-code;
the Block can be an empty shell, and we can generate DOM dynamically inside the Block with the appropriate event listeners;
Both ways
I can access the internal Model from the Reactive Web App from my high-code, by invoking Block client actions;
from my Javascript high-code, I can trigger Block events and implement communication to its parents;
Bundling and pre-processor
we can have our codebase split in multiple separate files with a proper tree folder structure, instead of one big monolithic file;
we can use Typescript with all its advantages;
we can embed the necessary CSS directly in the Javascript;
Namespacing
we can organize and group related code under a single and unique object;
we are not polluting the global window, and everything is inside a controlled namespace;
// create a global object
const MyAwesomeLibrary = {};
// and the components creation...
MyAwesomeLibrary.MyThing = class MyThing {
#property1;
#property2;
// ...
};
// usage on the OnReady
$parameters.Instance = new MyAwesomeLibrary.MyThing({
parameter1: $parameters.parameter1,
parameter2: $parameters.parameter2
});
Ufff… we have covered so much. 😮💨
But now we are ready for the good stuff, and we can start prompting our brains out!
Conclusion
Like I said at the beginning of the article, the industry has evolved a lot in the last year regarding AI capabilities.
At the same time, I believe proper Frontend Architecture will always be necessary when working with enterprise scalable applications.
It is possible to mix them and create better applications: OutSystems + Frontend Engineering + AI coding.
I hope this article will help you take advantage of that.
Similar Readings (5 items)
Install Cursor and Learn Programming With AI Help
Pair Programming with Cursor - Part 1
Nice — that “vibe working” news is interesting, and it shows how the meaning of “vibe” is evolving in tech. Let me explain what I found and what it might imply.
Claude Code: the command line gets agentic (but should it?)
Things That Senior Programmers Never Do with AI
Summary
Title: Using AI-Powered IDE in OutSystems with Cursor
The text discusses the use of an AI-powered Integrated Development Environment (IDE) named Cursor, specifically when integrated with OutSystems, to boost application development efficiency. Vibe-coding, a software development approach that
The text discusses the use of an AI-powered Integrated Development Environment (IDE) named Cursor, specifically when integrated with OutSystems, to boost application development efficiency. Vibe-coding, a software development approach that