<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: informat</title>
    <description>The latest articles on DEV Community by informat (@informat).</description>
    <link>https://dev.to/informat</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3901489%2Fab8d13c5-0932-420d-9f96-849a0743c7cd.png</url>
      <title>DEV Community: informat</title>
      <link>https://dev.to/informat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/informat"/>
    <language>en</language>
    <item>
      <title>Your Low-Code Platform Was Designed for Someone With a Desk</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Tue, 29 Sep 2026 01:37:24 +0000</pubDate>
      <link>https://dev.to/informat/your-low-code-platform-was-designed-for-someone-with-a-desk-28k1</link>
      <guid>https://dev.to/informat/your-low-code-platform-was-designed-for-someone-with-a-desk-28k1</guid>
      <description>&lt;p&gt;A year and a half ago, a manufacturing customer sent us a screenshot instead of a bug report.&lt;/p&gt;

&lt;p&gt;It was a phone. The screen showed one of our detail forms, rendered exactly as it renders on a 27-inch monitor, shrunk until the labels were unreadable. On top of it, someone had used a thumb to cross out half the fields. Below the screenshot, one sentence: our warehouse people use this all day, and they hate it.&lt;/p&gt;

&lt;p&gt;They did not ask for a mobile app. They assumed they already had one.&lt;/p&gt;

&lt;p&gt;That screenshot is still the most useful product feedback we have ever received, and it was never really about a phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every demo is a laptop
&lt;/h2&gt;

&lt;p&gt;Nobody ships a low-code platform demo on a phone. That is not laziness; it is that the phone is a terrible stage. A form designer, a workflow canvas, a permission tree, a dashboard — these are instruments that need a wide surface, and they are exactly what you show when you are explaining what the product &lt;em&gt;is&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So the mental model of the platform, from the earliest architecture conversation, is built around a person sitting down. A person with a keyboard, a mouse, ideally two monitors, and thirty uninterrupted minutes to configure a screen.&lt;/p&gt;

&lt;p&gt;Then the application gets deployed, and the person who actually uses it is standing in an aisle with a box in one hand.&lt;/p&gt;

&lt;p&gt;The gap between those two people is not a breakpoint. It is a different product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile is not a rendering target. It is a second view of the same metadata.
&lt;/h2&gt;

&lt;p&gt;The mistake we made — and the mistake I see in almost every platform — is treating mobile as a layout problem. Something CSS can fix. Add a media query, stack the columns, collapse the sidebar, ship it.&lt;/p&gt;

&lt;p&gt;That fails because a desktop screen and a phone screen are not the same information architecture at two scales. They are two different answers to the question: which of these forty fields does this person, in this moment, actually need?&lt;/p&gt;

&lt;p&gt;Look at a real detail view in an enterprise app. Identity fields, classification fields, financial fields, attachments, status, owner, related records, history. On a desktop, showing all of it is a courtesy. On a phone, showing all of it is an act of hostility. The person on the shop floor needs three of those. The person in finance needs a different three.&lt;/p&gt;

&lt;p&gt;Which means the platform has to answer something it has never had to answer: &lt;strong&gt;who is looking, and where are they standing?&lt;/strong&gt; That is not styling. That is metadata — a per-view, per-role, per-context statement of what matters.&lt;/p&gt;

&lt;p&gt;And once you accept that, the honest conclusion arrives quickly: the desktop layout you shipped for years was never designed either. It just had enough room to hide the fact that you never decided.&lt;/p&gt;

&lt;h2&gt;
  
  
  The table is where everything collides
&lt;/h2&gt;

&lt;p&gt;Forms are the easy part. The data table is where low-code platforms actually live — a grid of rows and columns is the most-used screen in every enterprise system ever built, and it is the one thing a phone genuinely cannot do.&lt;/p&gt;

&lt;p&gt;Forty columns. Frozen headers. Inline editing. Cross-page batch selection. Column-level permissions. Every one of those is a promise made to someone on a laptop.&lt;/p&gt;

&lt;p&gt;There are three ways out, and I have watched all three get chosen.&lt;/p&gt;

&lt;p&gt;You can reduce. Pick the five columns that matter for the mobile context, name that as a mobile view, and let the rest be reachable through the detail screen. This works, and it forces an admission: most grids are used as three columns and a search box.&lt;/p&gt;

&lt;p&gt;You can rotate. Tell the user to hold the phone sideways. That is a real answer for a real minority of cases and a terrible default, and it is a tell that you have not decided anything.&lt;/p&gt;

&lt;p&gt;Or you can pan. Let the grid scroll sideways and watch people lose track of which row they are on within the first three columns. I have never met a user who liked it.&lt;/p&gt;

&lt;p&gt;We ended on the first option, and it cost us something real: someone has to decide, per application, which five columns. That decision cannot be automated away, because it encodes business judgment about a job we do not do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The permission question hiding inside the layout question
&lt;/h2&gt;

&lt;p&gt;This is the part that took me too long to see.&lt;/p&gt;

&lt;p&gt;When you strip a mobile screen down to three fields, you are not only choosing what to show. You are deciding what the platform believes the client needs to hold.&lt;/p&gt;

&lt;p&gt;If the reduction happens in the browser — the field is fetched and then hidden with CSS — you have reduced nothing. You have shipped every confidential column in the salary table to a phone that someone left in a taxi. Responsive design has quietly become a data exfiltration path, and no security review catches it, because from the API's point of view nothing changed at all.&lt;/p&gt;

&lt;p&gt;The only safe version of this is the version where the mobile context is declared at the metadata layer, resolved on the server, and reflected in the query the platform actually runs. Which means mobile support cannot be a front-end project. It has to reach the same layer permissions reach.&lt;/p&gt;

&lt;p&gt;We learned that one the expensive way. I would rather you learned it from this paragraph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Most of your mobile users will never open your app
&lt;/h2&gt;

&lt;p&gt;The other thing we underestimated: enterprise mobile use does not look like consumer mobile use.&lt;/p&gt;

&lt;p&gt;Very few of our customers' field users browse to an application. They open WeCom, DingTalk, or Feishu, they see a message, they tap it. The notification &lt;em&gt;is&lt;/em&gt; the interface. The form and the grid are what happens after the tap, and they have to survive being embedded inside someone else's webview, with someone else's navigation bar, someone else's font scaling, and someone else's back button.&lt;/p&gt;

&lt;p&gt;That reframes the whole story. Mobile is not primarily about screens. It is about whether your platform can create a task that arrives as a message, opens in one tap, gets completed in ninety seconds by someone wearing gloves, and pushes its result back into a workflow that has been waiting since six in the morning.&lt;/p&gt;

&lt;p&gt;Which is a workflow question wearing a mobile costume.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we changed
&lt;/h2&gt;

&lt;p&gt;Not much of it was glamorous. We stopped pretending the phone was a smaller laptop: the mobile context became a first-class piece of view metadata that a builder defines on purpose and the server resolves before it runs anything. The reduction moved server-side, and we deleted the responsive hacks that had been quietly leaking columns to clients that never needed them.&lt;/p&gt;

&lt;p&gt;We made every form and grid openable through a tokenized link, so it can live inside a chat message instead of behind a navigation tree. We measured cold start on real hardware, because a phone on factory Wi-Fi is not a laptop on fiber, and a two-second desktop load is a ten-second one in the aisle.&lt;/p&gt;

&lt;p&gt;And we stopped letting the builder decide alone. The mobile view gets previewed on an actual phone, inside the actual app the customer already uses. Not a device emulator in a desktop browser. That distinction has caught more mistakes than any review meeting we have ever held.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;Here is what I actually believe after eighteen months of this.&lt;/p&gt;

&lt;p&gt;Low-code platforms sell speed, and speed is measured in how fast you can build a screen. But the screen was never the thing that mattered. What mattered was that the screen got used — by a specific person, in a specific place, with whatever was already in their hands.&lt;/p&gt;

&lt;p&gt;We spent years compressing the distance between an idea and a working application, and almost no time on the distance between a working application and a person who is not sitting down.&lt;/p&gt;

&lt;p&gt;The desktop was never the default. It was just the only context we had bothered to model — and for most of our customers' users, it is not where the work happens. It is where the work gets reviewed.&lt;/p&gt;

&lt;p&gt;The person holding the box is who your platform is actually for. If your metadata has no way to describe them, you did not build an enterprise platform.&lt;/p&gt;

&lt;p&gt;You built a very good demo.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>mobile</category>
      <category>ux</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Hardest Feature in a Low-Code Platform Is the Upgrade</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Mon, 28 Sep 2026 02:06:58 +0000</pubDate>
      <link>https://dev.to/informat/the-hardest-feature-in-a-low-code-platform-is-the-upgrade-41m7</link>
      <guid>https://dev.to/informat/the-hardest-feature-in-a-low-code-platform-is-the-upgrade-41m7</guid>
      <description>&lt;p&gt;Two weeks ago we shipped a release. A small one by our standards: a reworked lookup field, a smarter default for the date control, and a permission check moved one layer earlier in the write path.&lt;/p&gt;

&lt;p&gt;By Thursday afternoon, a customer's warehouse application had stopped assigning storage locations. Nobody had touched that app in eight months. The person who built it had left the company in March.&lt;/p&gt;

&lt;p&gt;The release did exactly what its notes said it would. It also broke something we had never opened.&lt;/p&gt;

&lt;p&gt;That was the week I stopped thinking about upgrades as a release-engineering problem, and started thinking about them as the hardest product decision a low-code platform makes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your release note is somebody's Tuesday morning
&lt;/h2&gt;

&lt;p&gt;When you ship a normal SaaS product, an upgrade is a deployment detail. The code is yours, the tests are yours, and the blast radius is measured in your own repository.&lt;/p&gt;

&lt;p&gt;A low-code platform is different in one specific and unforgiving way: your users are also your developers. Their applications are not source code you can refactor on a branch. They are metadata — tables, fields, validation rules, workflow steps, permission trees — assembled by people who will never read your changelog and did not ask to participate in your roadmap.&lt;/p&gt;

&lt;p&gt;Which means every release you ship is deployed to every application anyone has ever built on your platform. Including the one a contractor assembled in 2022 and abandoned. Including the one whose only purpose is that a single department needed a spreadsheet with approvals and an audit trail. Including the one that quietly became load-bearing infrastructure for a factory floor, while its author moved to a different team and forgot it existed.&lt;/p&gt;

&lt;p&gt;Most teams measure a change by its blast radius in the codebase. In a low-code platform, the blast radius is somebody's business process — and you do not have a test suite for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata is data. That is the entire problem.
&lt;/h2&gt;

&lt;p&gt;Here is the sentence I keep repeating to new engineers on the team, and the one that took me the longest to internalize: &lt;strong&gt;the application model is data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not "configuration." Not "the customer's workspace." Data. Rows in tables, written by users, referenced by other rows, with all the mess that implies.&lt;/p&gt;

&lt;p&gt;Once you accept that, the consequence is immediate and uncomfortable. Every change you make to the model is a data migration — on other people's data, in production, typically without a rollback path, and with a few thousand people downstream who find out by watching their app behave differently on Monday.&lt;/p&gt;

&lt;p&gt;A schema change you would happily make in a codebase becomes a decision with a completely different cost structure. Rename a field in your own codebase: the compiler tells you every place that broke. Rename a field on someone else's application: nothing tells you anything, and the workflow that referenced it fails silently at 2 a.m. on a Saturday.&lt;/p&gt;

&lt;p&gt;The version number on your release is not the version number that matters. What matters is the version of every application built on top of you, and you did not write those.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deprecation is a promise you make to strangers
&lt;/h2&gt;

&lt;p&gt;The warehouse incident turned out to be a lookup field that we had quietly improved. The new behavior was better in every case we had tested.&lt;/p&gt;

&lt;p&gt;The customer's app had been written against the old behavior, deliberately or not, and had been correct for eight months. Our release notes were honest. Our tests were green. And a person who has never met us inherited a broken app on a Thursday.&lt;/p&gt;

&lt;p&gt;That is what deprecation actually is. Not a line in a changelog. A promise you make to a stranger you will never meet, about a system they built and you will change.&lt;/p&gt;

&lt;p&gt;I used to think of backward compatibility as a box we ticked. Now I think of it as the primary surface of the product. The builder is the exciting part; the compatibility contract is the part that decides whether anyone dares to build anything serious.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three upgrade strategies, all expensive
&lt;/h2&gt;

&lt;p&gt;Every platform team eventually runs this experiment, and every one of the three answers costs something real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ship everything, all at once.&lt;/strong&gt; Fast for you, brutal for them. It works until it doesn't, and when it doesn't, you have broken a customer's operations with a fix you cannot un-ship. The velocity you gain is borrowed against trust you will need later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flag day: let the customer choose when.&lt;/strong&gt; Feels respectful, and it is. But it means you support the old behavior for as long as the slowest customer delays — and the customers who delay longest are, almost by definition, the ones whose applications are the most fragile and least understood. You have just guaranteed that your least maintained code paths serve your most at-risk users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compatibility forever.&lt;/strong&gt; Never remove anything. This is the most seductive option and the most corrosive. Within three years you are maintaining five subtly different behaviors for the same conceptual feature, your documentation describes a platform that no longer exists, and every new engineer has to learn all of it before they can change anything safely.&lt;/p&gt;

&lt;p&gt;We landed on a mix, and I would not pretend it is elegant: additive changes go out by default, behavioral changes get an announced window, and there is a short, explicit list of things we will simply not change without a customer-level conversation. The list is the important part. It is our promise written down, and it constrains us far more than it constrains them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we changed after that Thursday
&lt;/h2&gt;

&lt;p&gt;None of this is clever. All of it is the difference between a platform that customers trust with their core processes and one they merely tolerate.&lt;/p&gt;

&lt;p&gt;We started treating the application model as a versioned artifact. Before any release, we can diff a customer's workspace before and after, and see what would move. A behavioral change now gets run against real customer applications first, as a dry run, not just against our fixtures.&lt;/p&gt;

&lt;p&gt;We stopped letting destructive actions be silent. When a field is deleted or renamed, the platform shows the workflow steps, rules, and reports that referenced it — while the person doing the deletion still has the tool in their hand and the context in their head.&lt;/p&gt;

&lt;p&gt;We built the upgrade path into the product itself. Customers can see what changed and what it means to them, in their language, in their workspace. A blog post is not a migration plan.&lt;/p&gt;

&lt;p&gt;And we changed who we write release notes for. Not the person who built the app. The person who inherited it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;Every low-code platform is organized, from its first commit, around making things easy to &lt;em&gt;build&lt;/em&gt;. Almost none are organized around making things easy to &lt;em&gt;inherit&lt;/em&gt;. Building is where the demo lives, where the funding is, where the conference talk comes from.&lt;/p&gt;

&lt;p&gt;But here is what I resisted for a long time: you are not shipping software to a customer. You are the co-author of every application they ever built, permanently, whether or not you ever saw it. The upgrade path is not a maintenance concern bolted onto the product. The upgrade path &lt;em&gt;is&lt;/em&gt; the product, and it is the only part of your platform that every one of your customers will experience, on a Tuesday, without warning.&lt;/p&gt;

&lt;p&gt;We spent three years making the builder fast. The week the warehouse app broke was the week I understood that its maintainer — tired, new, and holding an application they did not build — was the customer we had been serving all along.&lt;/p&gt;

&lt;p&gt;Design the upgrade like it is the feature. Because for the person who inherits the app, it is the only feature they will ever notice.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>architecture</category>
      <category>devops</category>
      <category>enterprise</category>
    </item>
    <item>
      <title>The Expression Engine Is Small. That Is Exactly the Problem.</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Sun, 27 Sep 2026 01:46:15 +0000</pubDate>
      <link>https://dev.to/informat/the-expression-engine-is-small-that-is-exactly-the-problem-g14</link>
      <guid>https://dev.to/informat/the-expression-engine-is-small-that-is-exactly-the-problem-g14</guid>
      <description>&lt;p&gt;In June, a distributor customer of ours called about a quote that should never have existed. Their quoting app has a validation rule: the discount field must stay under thirty percent unless the record carries an approval flag. The rule had been there for a year. It was written by their finance lead herself, in the platform's formula editor, in about four minutes.&lt;/p&gt;

&lt;p&gt;The quote in question had a discount of eighty percent. It had been created by a bulk import from their old system, during a product-line migration. The validation formula had evaluated exactly zero times on its journey into the database. Not because the rule was broken — because the rule only ran where the form ran, and an import is not a form.&lt;/p&gt;

&lt;p&gt;Nobody had ever told the finance lead that. Nobody had ever told &lt;em&gt;me&lt;/em&gt; that, in those words, and I build the thing.&lt;/p&gt;

&lt;p&gt;That phone call rearranged how I think about a component I had mentally filed under "small": the expression engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  It is not one feature. It is six wearing a trench coat
&lt;/h2&gt;

&lt;p&gt;When you say "formula" in a low-code platform, most people picture the computed field. The little column that multiplies quantity by price. That is the least interesting instance.&lt;/p&gt;

&lt;p&gt;Look at where expressions actually live in a platform like ours. Default values on fields. Validation rules. Visibility conditions on form sections. Branch conditions in workflows. Filters in reports and list views. Threshold checks in automations. Six subsystems, and in the early version of our platform, six different ways of parsing what was supposed to be the same language.&lt;/p&gt;

&lt;p&gt;That last sentence should horrify you.&lt;/p&gt;

&lt;p&gt;Because here is the thing about an expression engine: it is not a feature. It is a &lt;em&gt;language&lt;/em&gt;, and a language that appears in six places is six chances to disagree with itself. The day a user discovers that &lt;code&gt;empty&lt;/code&gt; means one thing in a validation rule and something subtly different in a workflow branch is the day they stop trusting every formula they read.&lt;/p&gt;

&lt;p&gt;Unifying the parsers was tedious work with no visible payoff. It was also one of the highest-leverage things we have done: a user who learned the language once could finally predict the platform everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every formula is a tiny contract with a field that can disappear
&lt;/h2&gt;

&lt;p&gt;The second thing the discount incident taught me is that a formula is not text. It is a &lt;em&gt;dependency&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When the finance lead wrote her validation rule, she referenced a field called Approved. That reference is a promise: as long as this formula exists, the field it points at exists, and the platform knows the formula must re-evaluate when the field changes. Multiply that promise by a few thousand formulas across a customer's workspace and you have a dependency graph that nobody drew and everybody relies on.&lt;/p&gt;

&lt;p&gt;Now let a user delete or rename that field. What happens to the formula?&lt;/p&gt;

&lt;p&gt;The lazy answer is that the formula keeps the old text and breaks at runtime. A rule that looks fine in the editor and fails silently in production is the worst failure mode there is: no exception, just a quiet wrong answer, invisible in the place people check and wrong in the place they don't.&lt;/p&gt;

&lt;p&gt;The expensive answer is a real dependency graph. Deleting a field tells you what references it. Renaming a field updates every formula atomically. Formula edits are validated against the current schema at save time, not at evaluation time. And when a formula cannot be fixed automatically, the platform says so loudly, at the moment of the destructive action, while the person who can fix it is still holding the tool.&lt;/p&gt;

&lt;p&gt;None of this shows up on a feature list. All of it shows up in whether customers dare to evolve their apps after six months of use. A platform where users are afraid to rename fields is a platform that has quietly taught its users to stop building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Null is the real language
&lt;/h2&gt;

&lt;p&gt;Here is the part of expression engine design that consumed the most arguments per line of spec: what happens when a value is missing.&lt;/p&gt;

&lt;p&gt;Every business table is full of holes: a quote without a close date, an order where the discount field was never touched, a contact imported without a phone number. Formulas run into those holes all day long.&lt;/p&gt;

&lt;p&gt;So the questions arrive immediately. Is an empty text field null, or an empty string? Is an untouched number null, or zero? And crucially: does a validation rule that evaluates to &lt;em&gt;null&lt;/em&gt; pass or fail? We decided, after real deliberation, that a rule which cannot produce an answer should fail closed. A rule that says "this must be under thirty percent" cannot vouch for a number it cannot see. Our finance lead, when I explained this a month after the incident, said: "Obviously." It was not obvious. We chose it. She just happened to agree.&lt;/p&gt;

&lt;p&gt;Type coercion is the same story. A formula language that silently turns "10" into 10 is friendly for ten minutes and a liability for a decade, because the string that looks numeric is not always numeric. We chose to be strict and provide explicit conversion functions, and yes, users complained about the strictness for a quarter. Then they stopped filing the bugs that come from silent coercion.&lt;/p&gt;

&lt;p&gt;Dates deserve their own confession. We still carry scar tissue from formulas that computed "days overdue" differently depending on which server evaluated them, before we decided the language would treat a zoned instant and a plain calendar date as &lt;em&gt;different types&lt;/em&gt;, not one type with a surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a formula runs matters more than what it says
&lt;/h2&gt;

&lt;p&gt;The discount formula said exactly the right thing. It ran in the wrong place.&lt;/p&gt;

&lt;p&gt;The deep design question is not the grammar. It is the execution contract: which expressions run in the browser as the user types, and which run on the server in the write path.&lt;/p&gt;

&lt;p&gt;Visibility conditions want to be instant, so they run client-side. Validation, computed fields, and workflow branches &lt;em&gt;must&lt;/em&gt; run server-side, because the write path is the only place where every door leads through the same checkpoint. Forms, imports, API calls, automations — if the rule lives there, a discount of eighty percent cannot exist, no matter which door it came through.&lt;/p&gt;

&lt;p&gt;This sounds like an implementation detail. It is a philosophical one. A low-code platform makes a promise to every builder: the constraints you express are properties of the &lt;em&gt;data&lt;/em&gt;, not of one particular screen. The moment a rule is a property of a screen, you have shipped a suggestion, not a constraint. Suggestions do not survive contact with integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The security line nobody sees
&lt;/h2&gt;

&lt;p&gt;One more uncomfortable thing. An expression engine is one step away from being a scripting engine, and the distance is measured in "one more useful function." Users will ask for string manipulation, then lookups, then loops, then "just a small HTTP call." Each request is reasonable. The accumulation is a scripting engine with no review, no permissions, and no audit, running inside every form on the platform.&lt;/p&gt;

&lt;p&gt;We drew the line deliberately: expressions compute values over the record they are attached to. They get a whitelist of pure functions. Data from other tables arrives through explicit, permission-checked references — the same permission system that governs the UI, not a side door. The moment someone needs more, the answer is the scripting layer, with its own governance. Keeping that line bright is a permanent negotiation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;Here is what I resisted for years: a low-code platform's expression engine is its second programming language, and for most users of the platform, it is the &lt;em&gt;first&lt;/em&gt; one. More people will write formulas than will ever open the scripting editor. Its semantics — what null means, what a failed validation means, what a rename does — will outlive your API versioning, your theming, and half your features.&lt;/p&gt;

&lt;p&gt;We did not design it like a language. We designed it like a convenience, and the discount incident was the language reading its own terms back to us.&lt;/p&gt;

&lt;p&gt;The uncomfortable version is this: the smallest component in your platform is the one your users live in daily. They will never see the workflow engine's state machine or the query planner's choices. They will see your null handling, every afternoon, in every table.&lt;/p&gt;

&lt;p&gt;Design it like a language. Or one June, a phone call will design it for you.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>architecture</category>
      <category>programming</category>
      <category>enterprise</category>
    </item>
    <item>
      <title>Can Your Low-Code Platform Survive a Second Language?</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Sat, 26 Sep 2026 01:36:55 +0000</pubDate>
      <link>https://dev.to/informat/can-your-low-code-platform-survive-a-second-language-4ldd</link>
      <guid>https://dev.to/informat/can-your-low-code-platform-survive-a-second-language-4ldd</guid>
      <description>&lt;p&gt;In March this year, a customer of ours who runs a mid-sized manufacturing group called me about something that had nothing to do with features. They had spent two years building an ERP on our platform. Purchase orders, quality inspection, warehouse management, the works. It worked. Their people knew it. Then they acquired a factory overseas, and the new general manager there asked, very politely, one question during the handover meeting: "Can the system speak English?"&lt;/p&gt;

&lt;p&gt;I said yes. Of course I said yes. And then I spent the next two months learning exactly how much that "yes" was going to cost.&lt;/p&gt;

&lt;p&gt;Internationalization sounds like a checkbox. Add a language switcher, translate some labels, done. Every platform marketing page lists "multi-language support" in the same sentence as "multi-tenant" and "responsive layout." It sits there, harmless, one bullet among many.&lt;/p&gt;

&lt;p&gt;It is not a checkbox. It is an architectural confession. A second language does not test whether you can translate strings. It tests where language actually lives inside your platform — and in most low-code platforms, the honest answer is: everywhere, carelessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first thing that breaks is the schema, not the UI
&lt;/h2&gt;

&lt;p&gt;When people think of i18n, they think of interface text. Buttons, menus, placeholders. That part is genuinely easy. Any competent platform has a translation layer for its own chrome.&lt;/p&gt;

&lt;p&gt;But a low-code platform does not just ship a UI. It ships a &lt;em&gt;construction kit&lt;/em&gt;. The customer builds their own data models, their own forms, their own workflows, their own validation rules. So the question is not "can our menus speak English." The question is: when a customer renames a field, where does that name go?&lt;/p&gt;

&lt;p&gt;If the field label is a single string on the field definition, you have a problem. That string is simultaneously the display label, the thing the search engine indexes, the thing reports print in headers, and the thing AI-generated descriptions reference. When the second language arrives, you discover that "label" was never one thing. It was four things wearing one coat.&lt;/p&gt;

&lt;p&gt;The fix is not adding a second string. It is deciding that text visible to humans is a &lt;em&gt;localized object&lt;/em&gt; — a small map from locale to string, with a fallback chain, from the very first day you design the metadata model. Retrofitting that after a thousand tenants have built apps is one of the least pleasant exercises in software engineering. I have done adjacent versions of it. I do not recommend it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dropdowns are where optimism goes to die
&lt;/h2&gt;

&lt;p&gt;Here is the detail that humbled me. A form field of type "single select" has options. Each option has a display name. In the customer's original build, those option names were ordinary text: "Pending," "Approved," "Rejected" — except in Chinese, obviously.&lt;/p&gt;

&lt;p&gt;Now add English. Where does "Approved" live? If options are strings, someone has to duplicate every option list per language, and now the data stored is the Chinese string, so filtering "status equals Approved" silently returns nothing in the English UI. If you change the option to store an ID instead, you have migrated every workflow condition, every report filter, every script that compares against the old literal.&lt;/p&gt;

&lt;p&gt;We watched a variant of this happen in the customer's approval workflow. The workflow engine stored node names and branch conditions as text. The branch said: if department equals "采购部", route to procurement lead. In English UI, the department dropdown showed "Procurement." The condition never matched. Orders quietly fell into the default branch. Nothing errored. It just made wrong decisions, politely, for eleven days before anyone noticed.&lt;/p&gt;

&lt;p&gt;That is the real danger of i18n failures in low-code platforms. They do not throw exceptions. They translate the surface and leave the logic monolingual, and the system keeps running with a small, invisible misunderstanding at its core.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data itself refuses to be translated
&lt;/h2&gt;

&lt;p&gt;So you localize the schema. Good. Now the harder truth: the &lt;em&gt;data&lt;/em&gt; in the tables is whatever users typed. A product name entered as text by the Shanghai office is Chinese. The German salesperson opens the same record and sees Chinese. You can localize every label on the screen and the content is still in one language.&lt;/p&gt;

&lt;p&gt;Some of this you solve with structure — translation tables for reference data like categories, units, currencies. Some of it you honestly cannot solve, and the platform's job is to say so: separate what is &lt;em&gt;system text&lt;/em&gt; (translatable), what is &lt;em&gt;master data&lt;/em&gt; (translatable with discipline), and what is &lt;em&gt;user content&lt;/em&gt; (not translatable, and pretending otherwise creates a maintenance nightmare).&lt;/p&gt;

&lt;p&gt;And then there is the layer nobody budgets for: formats. Dates, numbers, names. Does your date field store a timezone or not? Does "03/04" mean March 4th or April 3rd depending on who is looking? Is a customer name one string, or a family name and a given name — because the Japanese and Icelandic users will fight you on that. These sound like trivia until they are payroll amounts or delivery dates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a platform owes its builders
&lt;/h2&gt;

&lt;p&gt;The uncomfortable part of this work was realizing that internationalization is not a feature you add to a low-code platform. It is a &lt;em&gt;contract&lt;/em&gt; you make with everyone who builds on it.&lt;/p&gt;

&lt;p&gt;The contract says: every piece of human-readable text you author — field labels, option names, workflow node names, validation messages, email templates, dashboard titles, automation notifications — is locale-aware from the moment you type it. You never see the machinery, but it is there. If we ship that contract late, every app built before it is silently in debt, and we cannot pay that debt for the customer. They have to pay it, record by record, option by option.&lt;/p&gt;

&lt;p&gt;This is also where low-code platforms differ from frameworks. A web framework hands i18n to the developer, who can adopt it per project. A platform like ours owns the metadata model for &lt;em&gt;every&lt;/em&gt; project ever built. We do not get to opt in per app. The architecture decision is platform-wide, permanent, and its cost lands on customers who trusted us.&lt;/p&gt;

&lt;p&gt;There is a second-order effect I did not anticipate: workflows and automations. A scheduled notification that says "您的订单已审批" is generated by a workflow definition. The report header that prints the wrong quarter because it formats dates with a hardcoded pattern. The AI agent that answers in Chinese because its prompt template was written in Chinese. Every subsystem that generates human-readable output is a translation surface, and each one you forget becomes a support ticket from a very polite manager in another country.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;I used to believe internationalization was a late-stage concern. Build the product first, prove the value, globalize later. That logic feels pragmatic. It is how almost every enterprise software company actually behaves.&lt;/p&gt;

&lt;p&gt;But here is what the two months taught me: you cannot retrofit "where language lives." You can retrofit translations. You cannot retrofit the decision that a field label is a map instead of a string, that an option stores an ID, that a workflow condition never compares display text. Those are not translations. They are the shape of your metadata, and the shape was poured in concrete on day one.&lt;/p&gt;

&lt;p&gt;The customer got their English interface. It works. It cost us far more than it would have cost as a design constraint nobody could feel at the time — because on day one, in one language, multi-language support looks like pure overhead with zero visible benefit.&lt;/p&gt;

&lt;p&gt;That is the trap, and it generalizes. The architectural decisions that matter most are exactly the ones whose cost is invisible when you skip them and expensive when you discover them. A second language is just the most polite messenger. It does not argue. It waits until you have a customer abroad, and then it reads your metadata model out loud.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>i18n</category>
      <category>architecture</category>
      <category>enterprise</category>
    </item>
    <item>
      <title>Who Changed This Record? The Question Your Low-Code Platform Must Answer</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Fri, 25 Sep 2026 02:31:14 +0000</pubDate>
      <link>https://dev.to/informat/who-changed-this-record-the-question-your-low-code-platform-must-answer-25eg</link>
      <guid>https://dev.to/informat/who-changed-this-record-the-question-your-low-code-platform-must-answer-25eg</guid>
      <description>&lt;p&gt;Last spring, I sat on a review call with a customer who had been running their entire HR operation on our platform for two years. Nothing was broken. No incident, no outage, no data loss. The HR director asked one question, almost apologetically: "An employee says her leave balance was wrong in March. Can you show me who changed the approval rule, and when?"&lt;/p&gt;

&lt;p&gt;I could not. Not in any form she could read, and not in any form an auditor would accept.&lt;/p&gt;

&lt;p&gt;We had logs. We had plenty of logs. What we did not have was an answer.&lt;/p&gt;

&lt;p&gt;That call rearranged my understanding of what an enterprise low-code platform actually owes its buyers. I had spent years treating audit capability as a checkbox — a nice-to-have that enterprise procurement departments mention in passing. I was wrong. It is closer to the foundation than the workflow engine, because without it, the workflow engine is a machine nobody can trust after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  We Had Logs. We Did Not Have an Answer.
&lt;/h2&gt;

&lt;p&gt;Here is the uncomfortable detail: our platform did record changes. Every rule edit, every permission tweak, every published configuration change landed somewhere in our infrastructure. If you gave me database access and twenty minutes, I could reconstruct what happened to that leave rule in March.&lt;/p&gt;

&lt;p&gt;But the HR director did not need a reconstruction. She needed a record.&lt;/p&gt;

&lt;p&gt;The difference matters more than it sounds. A reconstruction is something an engineer does, under time pressure, with privileged access, producing a conclusion nobody else can verify. A record is something the platform maintains continuously, in the language of the business, that any authorized person can read on their own. The first is a favor. The second is a system.&lt;/p&gt;

&lt;p&gt;Most low-code platforms, including mine for a long time, ship the favor and call it an audit trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Audit Trail Is Not a Debug Log
&lt;/h2&gt;

&lt;p&gt;When I finally sat down to design this properly, the first mistake I made was treating it as a logging problem. It is not. A debug log answers "what did the system do?" An audit trail answers "who did what, to which business object, when, and from where?" Those overlap less than you would think.&lt;/p&gt;

&lt;p&gt;The events that matter in a low-code platform are not error events. They are quiet, ordinary, business-shaped events. Someone lowered an approval threshold. Someone added themselves to a role. Someone changed a field from optional to required, or edited a validation formula, or published a new version of a form that three hundred employees use daily.&lt;/p&gt;

&lt;p&gt;Notice that a large portion of these are build-time events, not run-time events. This is where low-code differs sharply from traditional software. In a conventional application, the people who change behavior are engineers, and their changes travel through version control with commits, reviews, and blame. In a low-code platform, the change-makers are HR admins, operations managers, finance staff — people who will never open a git repository. The platform is their version control, whether it wants to be or not.&lt;/p&gt;

&lt;p&gt;If the platform does not keep a readable history of who changed what, it has quietly handed every customer the operational risk of production changes with none of the safety nets engineers take for granted.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hard Parts Nobody Puts in the Demo
&lt;/h2&gt;

&lt;p&gt;Designing the audit trail was humbling, because every part of it was harder than the slide in my head.&lt;/p&gt;

&lt;p&gt;The first hard part is deciding what deserves to be recorded. Record too little and the trail is useless exactly when it matters. Record everything and you drown the reader — a form save can touch a dozen objects, and a busy tenant generates thousands of events a day. We settled on a principle: record every event that changes meaning, not every event that changes bytes. A rename of a field label matters. An internal cache rebuild does not.&lt;/p&gt;

&lt;p&gt;The second hard part is readability. The first version of our audit log recorded raw identifiers: user IDs, object IDs, internal field names. Technically complete, practically useless. "User 8842 modified Field f_193 on Object obj_77" answers nothing. The record has to be written in the vocabulary of the customer: names, form titles, before-and-after values. That means the audit layer must resolve references at write time, because the objects it points to will themselves change or disappear later.&lt;/p&gt;

&lt;p&gt;The third hard part is immutability, or at least the credible appearance of it. An audit log that a tenant administrator can edit is not an audit log; it is a diary. Ours is append-only, with restricted deletion, and — this part took real argument internally — the audit log itself is subject to permissions. Not everyone who can view the HR app can view who changed the HR app. Treating the audit system as its own permission domain felt like over-engineering until the first customer asked whether their department managers could see records about salary formulas. They could not, and should not.&lt;/p&gt;

&lt;p&gt;The fourth hard part is retention and export. Enterprises do not want their audit history living forever inside your SaaS with no exit. They want to pull it into their SIEM, their compliance archive, their legal hold process. If your audit trail cannot leave your platform in a structured format, it is a view, not a record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability Is Not Just for Operations
&lt;/h2&gt;

&lt;p&gt;Somewhere in the middle of this work, I realized I had been thinking about observability too narrowly. I had borrowed the concept from operations — metrics, traces, logs for the running system — and assumed that was the whole story.&lt;/p&gt;

&lt;p&gt;But in a low-code platform, the "system" that misbehaves is often not the code. It is the configuration. The misbehavior is a permission added in April that quietly widened access, a workflow branch edited in June that started skipping a review step, an integration rewritten by someone who left the company in August. None of that shows up on a metrics dashboard. All of it shows up in a well-built audit trail.&lt;/p&gt;

&lt;p&gt;So for a platform like ours, the audit trail is not a compliance accessory next to the observability stack. It is the observability stack — for the layer where most of the real risk lives, which is the layer the customers themselves control.&lt;/p&gt;

&lt;p&gt;There is a second-order benefit I did not anticipate: the audit trail changed how customers use the platform. Once admins knew every change was attributed and visible, they started coordinating differently. Fewer silent edits. More use of draft-and-publish, because a publish event was now a visible, named act. Visibility did not just record behavior; it shaped it. That, I think, is the deepest argument for building this well — an audit trail is not just evidence after an incident, it is a discipline built into daily work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;The HR director's question cost us a quarter of engineering work to answer properly, and I have thought about why it took an incident to surface it.&lt;/p&gt;

&lt;p&gt;The honest answer is that audit capability has no champion during design. Nobody demos it. It wins no sign-ups, appears in no launch post, and the users who will depend on it do not know they need it until the day they desperately do. Every incentive in a competitive platform market pushes audit work to the bottom of the backlog. Ours sat there for years.&lt;/p&gt;

&lt;p&gt;The less comfortable answer is that I silently assumed our customers would always have engineers available to reconstruct history when needed. That assumption is not just wrong for low-code — it is the exact opposite of what low-code promises. We sell the idea that business people can safely own their systems. You cannot sell that and simultaneously sell them a platform where ownership without a memory.&lt;/p&gt;

&lt;p&gt;A platform that lets anyone change anything, and remembers nothing, is not empowering. It is an unlit warehouse.&lt;/p&gt;

&lt;p&gt;Build the lights in before the first tenant moves in.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>observability</category>
      <category>security</category>
      <category>enterprise</category>
    </item>
    <item>
      <title>Multi-Tenancy Is Not a Deployment Model. It Is a Data Model Decision.</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Thu, 24 Sep 2026 02:08:06 +0000</pubDate>
      <link>https://dev.to/informat/multi-tenancy-is-not-a-deployment-model-it-is-a-data-model-decision-4fg</link>
      <guid>https://dev.to/informat/multi-tenancy-is-not-a-deployment-model-it-is-a-data-model-decision-4fg</guid>
      <description>&lt;p&gt;The meeting lasted eleven minutes. The IT director of a manufacturing group had one question: "We have four subsidiaries. Can they all run on this, with each one only seeing its own data?"&lt;/p&gt;

&lt;p&gt;Everyone in the room heard a configuration request. It was an architecture request, and we spent the next year learning the difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three different things people call "tenant"
&lt;/h2&gt;

&lt;p&gt;The word hides at least three separate concerns, and the trouble starts when a customer uses it to mean all of them at once.&lt;/p&gt;

&lt;p&gt;There is a deployment tenant: a separate installation, a separate database, a separate upgrade. There is a data tenant: one installation, many logical partitions, one shared schema. And there is a configuration tenant: the same data boundary, but a different set of forms, workflows, and permission rules.&lt;/p&gt;

&lt;p&gt;Most platforms pick one of these and quietly assume the other two come along for free. Our customer wanted all three, for four subsidiaries, in one deployment, sharing one application template, with the ability to change that template for one subsidiary without touching the others.&lt;/p&gt;

&lt;p&gt;That last clause is where a low-code platform stops behaving like a normal SaaS product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata is data too
&lt;/h2&gt;

&lt;p&gt;In a conventional multi-tenant application, isolation is a filter. Every query carries a tenant column, every index starts with it, and the code review question is refreshingly boring: did you remember the where clause?&lt;/p&gt;

&lt;p&gt;A low-code platform does not have that luxury, because the thing being isolated is not only the customer's business records. It is the definition of the application itself: the fields, the views, the permission rules, the workflow versions, the automation triggers, the scripts, the dashboards. All of that is rows in tables too, and all of it is tenant-scoped.&lt;/p&gt;

&lt;p&gt;So the question multiplies. When a user in subsidiary A saves a list view, does that saved filter belong to them, to their subsidiary, or to the template everyone shares? When subsidiary B's admin fixes a bug in a shared workflow, do the other three get the fix, or an unexpected change on a Tuesday afternoon?&lt;/p&gt;

&lt;p&gt;There is no universally correct answer. There is only an answer the customer can predict. That is the real deliverable, and it is much harder to produce than a where clause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customization is where the boundary leaks
&lt;/h2&gt;

&lt;p&gt;Here is the uncomfortable shape of the problem. The reason people buy a low-code platform is customization. The reason multi-tenancy is hard is customization. These are the same property.&lt;/p&gt;

&lt;p&gt;Every customization surface is a candidate isolation hole. Custom fields: whose field is it, and what happens when a shared report assumes it exists? Custom scripts: this is the worst one, because it is user-authored code running inside the platform's own process, with the platform's own privileges. A script written by subsidiary A that quietly reads "all orders" is not a bug in the script. It is a bug in the boundary, and the person who wrote it had no idea they were crossing a line, because from where they sat there was no line to see.&lt;/p&gt;

&lt;p&gt;We learned to think of it this way: the tenant boundary cannot be enforced by the application builder, because the application builder is a tenant. It has to be enforced by the runtime, at every point where metadata meets data, whether or not the caller is aware there is a boundary at all.&lt;/p&gt;

&lt;p&gt;That means the enforcement point is not one place. It is the query compiler, the script sandbox, the workflow engine, the automation scheduler, the file store, the export pipeline, and now the AI agent. Every one of those has to know who is asking, and none of them can be trusted to guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tenant that ruins it for everyone
&lt;/h2&gt;

&lt;p&gt;Then there is the failure mode nobody puts in the sales deck: one tenant making the product bad for everyone else.&lt;/p&gt;

&lt;p&gt;A shared runtime means shared connection pools, shared caches, shared background workers, shared rate limits. One subsidiary imports a two-million-row spreadsheet before lunch and the other three watch their list views crawl. One automation goes into a retry loop and eats the scheduler.&lt;/p&gt;

&lt;p&gt;In a single-tenant installation, this is a performance bug. In a multi-tenant one, it is a fairness bug, and fairness is much harder to reason about than throughput. You end up building per-tenant quotas, per-tenant queues, and a notion of priority that never appeared in any requirement, because the customer never asked "can we be slow?" They asked to share a system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leaving is harder than joining
&lt;/h2&gt;

&lt;p&gt;The part nobody plans for is exit.&lt;/p&gt;

&lt;p&gt;When a tenant wants to leave — a contract ends, a subsidiary is sold, a regulation demands deletion — you have to answer a question the design has been avoiding for two years: what exactly belongs to them?&lt;/p&gt;

&lt;p&gt;Their business records, obviously. But also their attachments, their audit history, their script versions, their workflow instances, the reference data someone imported in year one, and possibly rows inside a shared table they can no longer distinguish from the shared rows, because the design treated "shared" and "theirs" as the same column.&lt;/p&gt;

&lt;p&gt;A platform that cannot delete a tenant cleanly cannot onboard one confidently. We found the honest test of a tenant boundary is not "can they see only their own data." It is "can we remove them completely, without touching anyone else, and prove it."&lt;/p&gt;

&lt;p&gt;If you cannot run that test, you do not have a tenant boundary. You have a convention.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent does not know about tenants
&lt;/h2&gt;

&lt;p&gt;The newest pressure came from somewhere we did not expect: the AI agent.&lt;/p&gt;

&lt;p&gt;An agent that can query, create, and edit records is the most capable user the platform has ever had, and also the least aware of context. A human in subsidiary A has a session, a sidebar, and a mental model of where they are standing. A tool call has a scope parameter, and if that scope is missing, wrong, or inherited from the wrong place, the agent will answer a question about subsidiary B's revenue in exactly the same confident tone it uses for everything else.&lt;/p&gt;

&lt;p&gt;We had to stop treating the tenant as a part of the request and start treating it as part of the identity. The agent does not get to say who it is acting for; the runtime tells it, and the runtime refuses anything that would cross a line — including the seemingly innocent requests, like "summarize all orders in the system," which sounds reasonable until you remember that the system has four customers inside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;We set out to build a platform where every customer feels like an owner. Multi-tenancy is the price of that feeling, and it is paid in the one currency we were least willing to spend: the assumption that the platform is only ever running for one person.&lt;/p&gt;

&lt;p&gt;The uncomfortable conclusion is that the feature we sell — deep, per-customer customization — is precisely the property that makes a tenant boundary unenforceable by policy. You cannot document your way out of it, because the document would have to be read by the person customizing the application, who is the one person who cannot see the line.&lt;/p&gt;

&lt;p&gt;The only boundary that holds is the one the platform enforces on itself, permanently, in every path where a definition becomes a query or an action. The customer gets to stop thinking about it.&lt;/p&gt;

&lt;p&gt;Which means, once again, we do not.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>saas</category>
      <category>architecture</category>
      <category>security</category>
    </item>
    <item>
      <title>Your Low-Code Platform Is Fast Until a Customer Builds One Real Table</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Wed, 23 Sep 2026 02:11:43 +0000</pubDate>
      <link>https://dev.to/informat/your-low-code-platform-is-fast-until-a-customer-builds-one-real-table-2mlk</link>
      <guid>https://dev.to/informat/your-low-code-platform-is-fast-until-a-customer-builds-one-real-table-2mlk</guid>
      <description>&lt;p&gt;The demo table had 412 rows. It loaded in under a second. Everyone in the room nodded.&lt;/p&gt;

&lt;p&gt;Eight months later, that same customer's work order table had 4.1 million rows. The list view took fourteen seconds to draw its first page. The customer's operations manager opened a support ticket with one sentence: "The system is slow."&lt;/p&gt;

&lt;p&gt;The interesting part is not that a platform got slow. It is where it got slow, and why that place was inevitable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The list view is where every abstraction converges
&lt;/h2&gt;

&lt;p&gt;A form is one record. A workflow is one instance. A dashboard is an aggregate someone designed to be cheap.&lt;/p&gt;

&lt;p&gt;A list view is different. It is the one screen where everything the platform promised has to be resolved at the same time: the field definitions, the relationships between tables, the permission rules, the workflow status, the computed columns, the saved filters, the sort order, and the user's own idea of what "the important records" are.&lt;/p&gt;

&lt;p&gt;In a demo, all of those layers are thin. Twelve fields, one related table, two hundred rows, one user with full access, no saved filters, default sort.&lt;/p&gt;

&lt;p&gt;In production, the same screen has eighty fields across four related tables, a row-level permission rule that depends on the user's department, and a saved filter someone created two years ago that nobody has looked at since.&lt;/p&gt;

&lt;p&gt;The design did not change. The definition of "fast" did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four walls, in the order you hit them
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Filter translation.&lt;/strong&gt; In the interface, a filter says "Amount is greater than 10000." In the database, it says nothing, because the platform does not own physical columns — it owns metadata. It has to compile user intent into a query. That compilation is where the first bug lives, because a field's type is not fixed. The same logical field can be numeric in one application and text in another, and the string comparison and the numeric comparison return different records. A filter that silently returns the wrong rows is worse than a filter that is slow, because slow gets reported and wrong does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission-aware filtering.&lt;/strong&gt; This is the one that breaks the performance model permanently. In a low-code platform, permissions are not a wrapper around the query. They are part of the query. If a user can only see records in their own department, then every count, every page, every aggregate, and every export must carry that restriction.&lt;/p&gt;

&lt;p&gt;The tempting shortcut is to fetch and then filter in application code. It works beautifully at four hundred rows and catastrophically at four million, and it introduces a quieter failure: the pagination gets confused. Total count says two thousand, the user sees sixty records per page, and page nine is empty. Then someone builds a report on the same numbers and the report disagrees with the screen, and now you are debugging a trust problem, not a latency problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related field display.&lt;/strong&gt; Showing "Customer Name" on a list of orders is a join. Most platforms hide this until the customer asks for their fifth related column, and then it becomes a join graph. The natural implementation is one query per row, which is elegant and dies at scale. The natural correction is one enormous query with twenty joins, which the planner may handle, until the day it does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sorting and computed columns.&lt;/strong&gt; Sorting by a stored, indexed column is cheap. Sorting by a computed column — a formula, a rollup from a child table, a workflow-derived status — means the database cannot use an index at all. It must evaluate the expression for every candidate row before it can decide what the first page is. At ten thousand rows nobody notices. At a million rows the sort is the query.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that took me too long to accept
&lt;/h2&gt;

&lt;p&gt;Performance work in a low-code platform is not a database administration problem. It is a metadata problem.&lt;/p&gt;

&lt;p&gt;You cannot hand your slow queries to a DBA, because at the moment the customer complains, the query does not exist yet. It gets generated at runtime out of a form definition, a permission rule set, and a filter someone saved in 2024. There is no query to tune.&lt;/p&gt;

&lt;p&gt;So you end up building a small query planner. You end up with a cost model that knows the shape of the permission rule, because a permission filter that resolves to an indexed column is a different animal from one that resolves to a subquery over an access graph. You end up with a plan cache, because compiling metadata to a query is not free and doing it on every request is absurd. You end up with a concept of which view configurations are "materializable" and which must be computed live.&lt;/p&gt;

&lt;p&gt;Nobody puts "build a miniature database optimizer" on a low-code platform roadmap. It shows up anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually moved the needle
&lt;/h2&gt;

&lt;p&gt;Four things, in order of impact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop assembling the view at request time. Compile it.&lt;/strong&gt; The combination of view definition, permission shape, and filter set is a query plan, and it can be built once and cached. The list view became a compiled artifact rather than a runtime negotiation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the permission filter cheap.&lt;/strong&gt; Precompute the visible set, or denormalize the scope into a column that can be indexed. The rule should resolve to a seek, not a traversal. This single change took more time off the worst screens than any index we added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Admit that two engines exist.&lt;/strong&gt; An interactive list view and a data export have completely different jobs. One must return a first page in a few hundred milliseconds, because a human is watching. The other may legitimately take thirty seconds, because nobody is. Trying to run both through one code path is how you get a system that is slow at three hundred rows and times out at ten million.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop promising deep pagination.&lt;/strong&gt; Offset pagination into the millions is a promise no database keeps. Deep pages need a cursor based on the sort key, not an arithmetic skip.&lt;/p&gt;

&lt;p&gt;And one thing that is not performance work at all but changed how users felt about the platform: telling the truth in the interface. A message saying the filter matched an enormous number of records and should be narrowed is a better experience than a spinner, even when both take three seconds. Users forgive slow. They do not forgive unexplained slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent made it worse before it made it better
&lt;/h2&gt;

&lt;p&gt;We added the ability for an AI agent to query, create, edit, and delete records against these same tables. It is genuinely useful. It is also the most aggressive list view user you will ever have.&lt;/p&gt;

&lt;p&gt;A human scrolling a slow table gives up after two pages. An agent does not give up, and it does not understand that asking for "all matching records" on a table with four million rows is a different request from asking for the first twenty. Almost everything we learned about interactive performance had to be re-learned as a machine-facing contract: hard limits, mandatory cursors, and a refusal to answer unbounded questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;Low-code platforms are sold on the promise that you no longer need to think about databases. That promise is real for the customer, and it is a lie for us.&lt;/p&gt;

&lt;p&gt;Someone still has to think about indexes, cardinality, execution plans, write amplification, and permission scope acting as an access path. The only thing the platform changes is who: it cannot be the person building the application. It has to be the platform itself, permanently, on behalf of users who will never know there was a decision to make.&lt;/p&gt;

&lt;p&gt;Which means every abstraction we sell is a debt instrument. The form engine, the permission system, the workflow engine, the plugin mechanism — each one buys simplicity at the top of the stack and pays for it in complexity at the bottom. The list view is simply where the bill arrives, and it arrives in year two, when the platform is load-bearing, the original demo is forgotten, and nobody remembers that the first version was fast because the table was small.&lt;/p&gt;

&lt;p&gt;I used to think of performance as a phase. Something you do after the features work.&lt;/p&gt;

&lt;p&gt;It is not a phase. It is the cost of the abstraction, and it comes due every time a customer's business succeeds enough to produce real data.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>database</category>
      <category>performance</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Every Low-Code Platform Eventually Becomes an Integration Platform</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Tue, 22 Sep 2026 13:49:52 +0000</pubDate>
      <link>https://dev.to/informat/every-low-code-platform-eventually-becomes-an-integration-platform-3no8</link>
      <guid>https://dev.to/informat/every-low-code-platform-eventually-becomes-an-integration-platform-3no8</guid>
      <description>&lt;p&gt;In my last post about release processes, I made a claim that felt like a conclusion: a low-code platform that cannot safely change is not enterprise software.&lt;/p&gt;

&lt;p&gt;This week I realized that claim was missing half the picture.&lt;/p&gt;

&lt;p&gt;A platform that cannot safely change will lose enterprise customers eventually. But a platform that cannot safely &lt;strong&gt;connect&lt;/strong&gt; will lose them immediately.&lt;/p&gt;

&lt;p&gt;Here is the pattern I have now seen enough times to call it a law: every low-code platform eventually becomes an integration platform. No exceptions. The only question is whether you admit it early or discover it in a panic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The conversation that repeats with every customer
&lt;/h2&gt;

&lt;p&gt;Month one with a new enterprise customer is wonderful. We build their data model, their forms, their approval workflows. Everything lives inside the platform. It is clean, fast, and entirely ours.&lt;/p&gt;

&lt;p&gt;Month two always starts with the same sentence:&lt;/p&gt;

&lt;p&gt;"This is great. Now can it talk to our existing system?"&lt;/p&gt;

&lt;p&gt;Their existing ERP. Their HR system. Their email server. Their legacy order database from 2011 that nobody wants to touch but everybody depends on.&lt;/p&gt;

&lt;p&gt;The first time, I thought this was one customer's unusual situation. The third time, I saw the shape of it. The fifth time, I stopped being surprised and started rebuilding the platform around it.&lt;/p&gt;

&lt;p&gt;Inside a low-code platform, everything is connected by design. A field on a form is bound to a column in a table. A workflow step reads a field. A dashboard aggregates a table. The connections are internal, typed, and always available.&lt;/p&gt;

&lt;p&gt;The moment a customer's data lives partly in your platform and partly somewhere else, all of that elegance meets the outside world. And the outside world does not have your field IDs, your permission model, or your transaction guarantees.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: point-to-point integrations
&lt;/h2&gt;

&lt;p&gt;The natural first response is to build each requested connection by hand.&lt;/p&gt;

&lt;p&gt;Customer needs order data in their ERP? Write a script that pushes orders to the ERP. Customer wants employee data from HR? Write another script that pulls from HR. Customer wants a notification in their messaging tool? One more script.&lt;/p&gt;

&lt;p&gt;This works. For a while.&lt;/p&gt;

&lt;p&gt;Then you count. Five external systems, each needing data from two or three others. That is not five integrations. That is a graph, and the edges multiply. Someone changes a field name in the HR system and three of your scripts break silently, and the only person who notices is the HR manager whose monthly report now has empty columns.&lt;/p&gt;

&lt;p&gt;The problem is not the number of scripts. It is that each script is a private agreement between two systems, with no shared vocabulary, no shared error handling, and no shared place to look when something goes wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  What integration actually breaks down into
&lt;/h2&gt;

&lt;p&gt;Once I stopped counting integrations and started studying them, four distinct jobs appeared.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inbound: getting data in.&lt;/strong&gt; An external system creates or updates records in the platform. This needs a real API surface — not an internal endpoint repurposed, but a public one with the platform's own permission system attached. If the API does not respect the same rules as the UI, you now have two security models and one of them is wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outbound: getting data out.&lt;/strong&gt; The platform pushes changes to external systems. This is where two engineering problems hide that nobody warns you about.&lt;/p&gt;

&lt;p&gt;The first is retries. External systems fail. Networks fail. Your push happens at the exact moment their system restarts. Without automatic retry with backoff, that record is simply gone, and nobody knows.&lt;/p&gt;

&lt;p&gt;The second is idempotency. The retry succeeds — but the first attempt had actually landed. Now the customer has two orders in their ERP for one order in your platform. An integration layer without idempotency keys is a machine for manufacturing duplicates at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Events: letting the outside world subscribe.&lt;/strong&gt; This is the one I resisted longest.&lt;/p&gt;

&lt;p&gt;For a long time I thought events were an advanced feature. Then I watched a customer build a "sync job" that polled our API every five minutes, compared timestamps, and pulled anything new. It worked. It was also fragile, slow, and completely blind to deletions.&lt;/p&gt;

&lt;p&gt;The platform already knows when a record is created, updated, or deleted. It already knows when a workflow step completes. It knows these things at the moment they happen. Emitting an event — record created, workflow finished, status changed — costs almost nothing, and it turns every polling script into a subscription.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secrets: where the credentials live.&lt;/strong&gt; An integration needs an API key, a token, a password. Where does it go?&lt;/p&gt;

&lt;p&gt;Not in a script. Not in a form field. Not in a configuration value visible to anyone with designer access.&lt;/p&gt;

&lt;p&gt;I learned this one embarrassingly late. Credentials belong in a dedicated, encrypted, access-controlled store that the integration layer reads but no script or page can dump. The first time a customer's auditor asks "who can see the ERP credentials," the answer must be a role, not "whoever can open the settings page."&lt;/p&gt;

&lt;h2&gt;
  
  
  The quiet problem: knowing when it broke
&lt;/h2&gt;

&lt;p&gt;The hardest part of integration is not sending data. It is knowing that sending data failed.&lt;/p&gt;

&lt;p&gt;An internal workflow fails, and the approver sees it. A form breaks, and users complain immediately. The feedback loop is built in.&lt;/p&gt;

&lt;p&gt;An outbound sync fails at 2 a.m. on a Saturday, and everything looks fine in the platform. The dashboard shows clean numbers. The workflows run green. Meanwhile, three days of orders never reached the ERP, and the finance team finds out during month-end reconciliation.&lt;/p&gt;

&lt;p&gt;So the integration layer needs its own observability. Every connection needs a visible run history, an error state that surfaces in the platform instead of in someone's inbox a week later, and a dead-letter state for events that could not be delivered — with a replay button. Not logs buried in a server. A screen that an operations person can check in ten seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;I used to describe our platform as a place to build business systems. Tables, forms, workflows, dashboards. A self-contained world.&lt;/p&gt;

&lt;p&gt;That description was accurate and useless, because no enterprise lives in a self-contained world.&lt;/p&gt;

&lt;p&gt;The real promise of a low-code platform is not that it replaces the customer's existing systems. It usually cannot, and the customer does not actually want it to. The promise is that it becomes the fastest place to build the connective tissue between the systems they already have — with permissions, audit, and rollback included.&lt;/p&gt;

&lt;p&gt;Which means the integration layer is not a feature on the roadmap.&lt;/p&gt;

&lt;p&gt;It is the reason the platform gets adopted, and the reason it gets trusted. The platforms that treat it as a checklist item spend their second year doing archaeology on broken scripts. The ones that treat it as core architecture spend their second year onboarding customers who already have five systems and no patience for another island.&lt;/p&gt;

&lt;p&gt;I know which second year I want.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>integration</category>
      <category>architecture</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Configuration Is Code. So Why Does Your Low-Code Platform Have No Release Process?</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Tue, 22 Sep 2026 09:09:20 +0000</pubDate>
      <link>https://dev.to/informat/configuration-is-code-so-why-does-your-low-code-platform-have-no-release-process-2hlc</link>
      <guid>https://dev.to/informat/configuration-is-code-so-why-does-your-low-code-platform-have-no-release-process-2hlc</guid>
      <description>&lt;p&gt;In my last post about scripting, I ended with an uncomfortable conclusion: once a low-code platform lets people write scripts, the platform is no longer a form builder. It is a programming environment.&lt;/p&gt;

&lt;p&gt;That conclusion raised a bigger question, and it took me a while to admit it.&lt;/p&gt;

&lt;p&gt;If configuration can express business logic, then configuration is code.&lt;/p&gt;

&lt;p&gt;And if configuration is code, why does nobody on a low-code platform get any of the things we take for granted when writing code?&lt;/p&gt;

&lt;p&gt;No version control. No diff. No review. No staging. No rollback.&lt;/p&gt;

&lt;p&gt;We give them a Save button and call it a day.&lt;/p&gt;

&lt;p&gt;That is a strange place for an enterprise platform to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  The day a customer asks to roll back
&lt;/h2&gt;

&lt;p&gt;Here is the scenario that changed how I think about this.&lt;/p&gt;

&lt;p&gt;A customer spends two weeks adjusting a purchase approval workflow. A new node, a new approver rule, a branch for amounts over a certain threshold. Everything looks right in testing. They publish on a Friday afternoon.&lt;/p&gt;

&lt;p&gt;On Monday, the routing is wrong for a subset of orders. Not catastrophically wrong. Just wrong enough that a few orders sat untouched over the weekend.&lt;/p&gt;

&lt;p&gt;The customer asks the natural question: can we go back to Friday morning's version?&lt;/p&gt;

&lt;p&gt;Silence.&lt;/p&gt;

&lt;p&gt;There is no Friday morning version. There is a workflow editor showing the current state, and a database holding the current rows. Nobody wrote down what it looked like before.&lt;/p&gt;

&lt;p&gt;The fix is to open the workflow editor and rebuild it from memory.&lt;/p&gt;

&lt;p&gt;That is not a rollback. That is an incident handled by archaeology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is genuinely harder in low-code
&lt;/h2&gt;

&lt;p&gt;The temptation is to say "just add version control." Anyone who has used Git assumes this is a weekend project.&lt;/p&gt;

&lt;p&gt;It is not, and the reason is structural.&lt;/p&gt;

&lt;p&gt;In a normal codebase, the unit of change is a file. Files are self-contained, they have paths, and a diff has an obvious meaning: these lines, in this order.&lt;/p&gt;

&lt;p&gt;In a low-code platform, the unit of change is a row in a table.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A data model is a record in an &lt;code&gt;entities&lt;/code&gt; table, and each field is a record in a &lt;code&gt;fields&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;A form is a layout record that points at field records by ID.&lt;/li&gt;
&lt;li&gt;A workflow is a graph: nodes in one table, edges in another, conditions in a third.&lt;/li&gt;
&lt;li&gt;Permissions are rules that reference roles, departments, and field IDs.&lt;/li&gt;
&lt;li&gt;A script is text that refers to fields by name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not one of these is a file. Not one of them can be diffed line by line, because there are no lines. And they all reference each other through generated IDs that mean nothing outside the environment where they were created.&lt;/p&gt;

&lt;p&gt;So when someone flips a "publish date" field from optional to required, the real question is not "what is the diff?"&lt;/p&gt;

&lt;p&gt;It is: which forms render this field? Which workflow conditions read it? Which report filters count on it? Which saved views sort by it?&lt;/p&gt;

&lt;p&gt;In a file-based world, &lt;code&gt;grep&lt;/code&gt; answers that in a second. In a row-based world, nothing answers it until you build the dependency graph yourself.&lt;/p&gt;

&lt;p&gt;That dependency graph is the actual product. The diff is only its visible surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three layers, and most platforms stop at the first
&lt;/h2&gt;

&lt;p&gt;Once I stopped thinking of this as "add Git" and started thinking of it as "make configuration changeable safely," the problem decomposed into three layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer one: snapshots.&lt;/strong&gt; A full, immutable copy of the configuration at a point in time. Cheap to build, crude to use. Nobody wants to restore an entire environment because one field permission changed. But if this layer does not exist, you have nothing to fall back on at all. It is the emergency brake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer two: changesets.&lt;/strong&gt; Instead of snapshotting everything, record the delta. Every publish produces a changeset with an author, a timestamp, a description, and a list of atomic changes. Now rollback means reverting a changeset, not rebuilding a workflow from memory.&lt;/p&gt;

&lt;p&gt;This is also where review becomes possible, and that matters more than it sounds. In most enterprises today, one person with workflow editor access can silently change how money moves through the company. A changeset with a visible diff turns that from an invisible act into a reviewable one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer three: promotion.&lt;/strong&gt; This is the layer almost everyone skips, and it is the one enterprise buyers actually ask about.&lt;/p&gt;

&lt;p&gt;They want three environments. Development for building. UAT for the business to test. Production for real work. And they want a controlled path from one to the next.&lt;/p&gt;

&lt;p&gt;That sounds like deployment. In a row-based configuration model, it is a genuinely hard identity problem.&lt;/p&gt;

&lt;p&gt;The field ID in development is not the field ID in UAT. The role ID in UAT is not the role ID in production. So a changeset cannot be a list of ID references. It has to be a semantic description — this field, identified by this stable key, on this entity — which the target environment resolves locally.&lt;/p&gt;

&lt;p&gt;Which means the platform needs stable, human-meaningful keys for every configurable object from day one. If you start with auto-increment IDs and try to bolt on promotion later, you will spend months mapping things back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The line nobody draws early enough: configuration versus data
&lt;/h2&gt;

&lt;p&gt;Here is the trap that costs the most, and it is not a technical one.&lt;/p&gt;

&lt;p&gt;Every low-code platform eventually has a table that is half configuration and half data, and nobody notices until the first production promotion.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;status&lt;/code&gt; list attached to a workflow? Configuration. It should ship.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;customer_level&lt;/code&gt; reference table the business maintains by hand? That is data. It must not ship.&lt;/p&gt;

&lt;p&gt;A currency exchange rate table? Data. Does not ship.&lt;/p&gt;

&lt;p&gt;A list of legal entity names used in a dropdown? Honestly, it depends on the customer.&lt;/p&gt;

&lt;p&gt;If you never explicitly decided which is which, the first promotion from UAT to production will overwrite live business data with test data.&lt;/p&gt;

&lt;p&gt;It is not a subtle bug. It is the kind of bug that ends a pilot.&lt;/p&gt;

&lt;p&gt;The rule I have landed on: if a business user can edit it in the running system and would be angry to lose it, it is data, and promotion must never touch it. Everything else is configuration.&lt;/p&gt;

&lt;p&gt;It sounds obvious written down. It took me an embarrassing amount of time to write it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Draft is not staging
&lt;/h2&gt;

&lt;p&gt;There is one more distinction I had to learn the hard way.&lt;/p&gt;

&lt;p&gt;Some platforms do have a draft mode. You edit the application in a designer, then hit Publish, and the changes go live. It feels like a release process.&lt;/p&gt;

&lt;p&gt;It is not one.&lt;/p&gt;

&lt;p&gt;A draft is a working copy. It tells you what is about to change, which is genuinely useful. But it does not give you a history, it does not let you compare two states, it does not let you promote one application's changes to a different environment, and it does not let you revert.&lt;/p&gt;

&lt;p&gt;Draft mode answers "what am I about to ship." A release process answers "what did we ship, who approved it, what changed, and how do we undo it."&lt;/p&gt;

&lt;p&gt;These are different questions. Only one of them survives an audit, and only one of them survives a Monday morning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable conclusion
&lt;/h2&gt;

&lt;p&gt;I started out thinking of the low-code platform as a builder. Tables, forms, workflows, permissions. A faster way to produce a business system.&lt;/p&gt;

&lt;p&gt;I now think that is only half of it.&lt;/p&gt;

&lt;p&gt;The other half is that we are asking non-engineers to do engineering work, without handing them any of the safety equipment engineers rely on. And when something goes wrong, we act surprised.&lt;/p&gt;

&lt;p&gt;Which means the platform has to supply that safety equipment itself.&lt;/p&gt;

&lt;p&gt;Not because customers ask for version control. They will not. They will ask exactly once — on the Monday morning after the Friday afternoon publish. And at that moment, "we don't have that" is not an answer that keeps an enterprise account.&lt;/p&gt;

&lt;p&gt;If configuration is code, then it deserves what code deserves: a history, a diff, a review, a rollback, and a path from test to production.&lt;/p&gt;

&lt;p&gt;Anything less is a Save button pretending to be a release process.&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>architecture</category>
      <category>devops</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>How Should Scripting Work in a Low-Code Platform?</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Mon, 10 Aug 2026 13:41:58 +0000</pubDate>
      <link>https://dev.to/informat/how-should-scripting-work-in-a-low-code-platform-1mfn</link>
      <guid>https://dev.to/informat/how-should-scripting-work-in-a-low-code-platform-1mfn</guid>
      <description>&lt;p&gt;In the previous post about automation, I ended with a question:&lt;/p&gt;

&lt;p&gt;Once automation connects business actions, where should the complex logic live?&lt;/p&gt;

&lt;p&gt;The answer is scripts.&lt;/p&gt;

&lt;p&gt;Scripting is easily misunderstood in a low-code platform.&lt;/p&gt;

&lt;p&gt;Some people ask why a low-code product needs scripts at all. Others worry that adding scripts turns low-code back into traditional software development.&lt;/p&gt;

&lt;p&gt;My view is that scripting is not the opposite of low-code.&lt;/p&gt;

&lt;p&gt;It is the controlled escape hatch that a low-code platform needs when it encounters genuinely complex business requirements.&lt;/p&gt;

&lt;p&gt;The important questions are not whether scripts exist. They are where scripts run, how they are invoked, which boundaries apply, and whether the platform can govern their lifecycle.&lt;/p&gt;

&lt;p&gt;Without boundaries, scripts pull the platform back toward scattered custom code. Without scripts, the platform becomes rigid when it meets real enterprise complexity.&lt;/p&gt;

&lt;p&gt;A scripting system should not let users write arbitrary code anywhere. It should provide a controlled runtime for logic that cannot be expressed clearly through configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why low-code platforms need scripts
&lt;/h2&gt;

&lt;p&gt;Low-code configuration can handle a large share of common requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Field validation&lt;/li&gt;
&lt;li&gt;Conditional form behavior&lt;/li&gt;
&lt;li&gt;Approval workflows&lt;/li&gt;
&lt;li&gt;Permission control&lt;/li&gt;
&lt;li&gt;Automation orchestration&lt;/li&gt;
&lt;li&gt;Data queries and updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The platform should provide these capabilities directly whenever possible.&lt;/p&gt;

&lt;p&gt;Enterprise applications still contain logic that is difficult to express elegantly as configuration.&lt;/p&gt;

&lt;p&gt;Examples include complex quotation calculations, signing requests to external APIs, cleaning data across several tables, generating identifiers from historical rules, transforming imported data, validating purchase lines against maximum material prices, or assigning an owner based on industry, region, customer level, and sales workload.&lt;/p&gt;

&lt;p&gt;Trying to convert every one of these cases into visual configuration makes the configuration system increasingly complicated. Each new option requires UI, expressions, validation, compatibility rules, boundaries, and performance considerations.&lt;/p&gt;

&lt;p&gt;Eventually, the platform can reach an awkward outcome: in an attempt to avoid code, it creates a configuration language that is harder to understand than code.&lt;/p&gt;

&lt;p&gt;That is not the goal of low-code.&lt;/p&gt;

&lt;p&gt;Scripts should carry logic that the configuration system would otherwise express poorly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scripts should not swallow the entire business process
&lt;/h2&gt;

&lt;p&gt;Once scripting is available, the easiest mistake is to put everything inside it.&lt;/p&gt;

&lt;p&gt;Consider the actions that follow customer creation. One script could check for duplicates, assign an owner, update the customer, send notifications, call external services, and write logs.&lt;/p&gt;

&lt;p&gt;This is fast in the short term. A developer can open the editor and implement the complete path in one place.&lt;/p&gt;

&lt;p&gt;It is painful in the long term.&lt;/p&gt;

&lt;p&gt;Business users cannot see the process. Implementation teams have difficulty troubleshooting it. The automation log only says that a script ran. Permission boundaries are easier to bypass. Even changing a notification rule requires someone to search through code.&lt;/p&gt;

&lt;p&gt;I prefer a clear division of responsibility:&lt;/p&gt;

&lt;p&gt;Automation handles orchestration.&lt;/p&gt;

&lt;p&gt;Scripts handle complex logic.&lt;/p&gt;

&lt;p&gt;An automation can query data, invoke a script to calculate a result, then use that result to update records, send notifications, and return a response.&lt;/p&gt;

&lt;p&gt;A script should not consume the entire business process. It should behave like a calculation, validation, or transformation node inside automation.&lt;/p&gt;

&lt;p&gt;This preserves the flexibility of code without losing the visual governance of automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-side and client-side scripts need different boundaries
&lt;/h2&gt;

&lt;p&gt;The first distinction in a scripting system is where the code runs.&lt;/p&gt;

&lt;p&gt;Some scripts belong on the server. Others belong in the client. They solve different problems and require different security models.&lt;/p&gt;

&lt;p&gt;Server-side scripts are suited to business logic such as querying and writing data, calling external services, performing complex calculations, processing batches, and generating structured results.&lt;/p&gt;

&lt;p&gt;They run in the platform backend, where they can access a richer business context and where permissions, logging, timeouts, and error handling can be enforced consistently.&lt;/p&gt;

&lt;p&gt;For INFORMAT, JavaScript is the practical language for server-side scripting.&lt;/p&gt;

&lt;p&gt;It is familiar to frontend and full-stack developers, which reduces the learning curve. Most enterprise scripts are not large applications. They implement a business rule, data transformation, or integration call, and JavaScript is flexible and lightweight enough for those jobs.&lt;/p&gt;

&lt;p&gt;Server-side scripts access platform functions through the &lt;code&gt;informat&lt;/code&gt; object. Their runtime state is isolated by application so that one application's script context cannot leak casually into another.&lt;/p&gt;

&lt;p&gt;This makes server-side scripting especially useful in two areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business processing behind APIs&lt;/li&gt;
&lt;li&gt;Complex calculations that become too cumbersome in visual automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point is not to replace automation. It is to give automation an efficient execution layer when a step needs to calculate something, query something, transform data, sign a request, or assemble a complex result.&lt;/p&gt;

&lt;p&gt;Client-side scripts are better suited to interface behavior.&lt;/p&gt;

&lt;p&gt;A form may calculate one field immediately after another field changes. A button may update local page state. A custom control may need lightweight logic. The interface may display instant feedback based on user input.&lt;/p&gt;

&lt;p&gt;These actions are close to the user experience and should not always require a round trip to the server.&lt;/p&gt;

&lt;p&gt;Client-side scripting still needs restraint. It must not become a permission backdoor. A page script cannot be allowed to read data that the user cannot access or modify protected fields through a direct request.&lt;/p&gt;

&lt;p&gt;Client scripts should handle interaction and lightweight calculations. Actions that affect persistent data or business state should return to server-side scripts, automation, or governed platform actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;informat&lt;/code&gt; object is the capability gateway
&lt;/h2&gt;

&lt;p&gt;A business script cannot be only bare JavaScript.&lt;/p&gt;

&lt;p&gt;The language alone can manipulate strings, arrays, and objects, but it cannot participate in enterprise operations. Scripts need controlled access to platform capabilities.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;informat&lt;/code&gt; object is that gateway. It exposes platform, utility, and integration functions to scripts through a consistent interface.&lt;/p&gt;

&lt;p&gt;Its capabilities can be viewed in several groups.&lt;/p&gt;

&lt;p&gt;The first group covers platform business objects, including applications, companies, departments, designers, and BPMN workflows. These APIs give a script access to the relevant application, organization, and process context.&lt;/p&gt;

&lt;p&gt;The second group covers data and integration, including data sources, JDBC, HTTP, FTP, and LDAP. These functions let scripts communicate with external systems and existing enterprise infrastructure.&lt;/p&gt;

&lt;p&gt;The third group covers files and documents, including files, CSV, Excel, and PDF. Importing, exporting, handling attachments, and generating documents are routine parts of enterprise digital operations. They should not require every team to rebuild the same foundation.&lt;/p&gt;

&lt;p&gt;The fourth group contains general utilities and runtime support, such as dates, console logging, encoding, and email. These capabilities look ordinary, but scripts use them constantly.&lt;/p&gt;

&lt;p&gt;The fifth group provides AI capabilities through AI agents.&lt;/p&gt;

&lt;p&gt;This opens interesting possibilities. Scripts can use AI for text generation, content extraction, data explanation, or business suggestions instead of limiting themselves to deterministic calculations.&lt;/p&gt;

&lt;p&gt;AI output requires stricter boundaries. It should not be written directly into critical business data without validation. A safer design lets AI generate a candidate result, then uses scripts or automation to validate its structure and business constraints.&lt;/p&gt;

&lt;p&gt;In this model, scripting becomes more than a place to write code. It is a programmatic gateway to platform capabilities.&lt;/p&gt;

&lt;p&gt;That role differs from visual automation. Automation emphasizes visible process orchestration. Scripts emphasize logic and function calls. The two should cooperate instead of competing for the same responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code snippets belong inside local automation steps
&lt;/h2&gt;

&lt;p&gt;Automation also needs a local form of scripting. I think of this as a code snippet rather than an independent script module.&lt;/p&gt;

&lt;p&gt;A code snippet handles a specific piece of logic inside one automation step.&lt;/p&gt;

&lt;p&gt;Suppose an automation loops over purchase order lines and evaluates whether each material exceeds its maximum purchase price. A simple comparison belongs in a visual condition.&lt;/p&gt;

&lt;p&gt;But the rule may become more complicated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different suppliers use different pricing policies.&lt;/li&gt;
&lt;li&gt;Material categories allow different variance percentages.&lt;/li&gt;
&lt;li&gt;Purchase types use different approval thresholds.&lt;/li&gt;
&lt;li&gt;Historical pricing must use the latest valid quotation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Encoding all of this in visual conditions makes the automation difficult to read. A code snippet can calculate the result and return it to the automation branch.&lt;/p&gt;

&lt;p&gt;The value of a code snippet is that it keeps the canvas readable, gives complex calculations a clear home, and leaves the overall execution path visible in automation.&lt;/p&gt;

&lt;p&gt;This is the most natural relationship between scripts and automation.&lt;/p&gt;

&lt;p&gt;Automation decides what happens first, what happens next, and what should occur after failure.&lt;/p&gt;

&lt;p&gt;The code snippet decides how one particular step is calculated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Script functions should be reusable
&lt;/h2&gt;

&lt;p&gt;Logic used in one place can remain a code snippet. Logic shared by several automations, buttons, or listeners should become a script function.&lt;/p&gt;

&lt;p&gt;Common examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating a business identifier&lt;/li&gt;
&lt;li&gt;Calculating a quotation&lt;/li&gt;
&lt;li&gt;Validating inventory&lt;/li&gt;
&lt;li&gt;Parsing an external API response&lt;/li&gt;
&lt;li&gt;Building notification content&lt;/li&gt;
&lt;li&gt;Converting imported data into a standard structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These functions should not be copied into every automation. They should be reusable platform assets.&lt;/p&gt;

&lt;p&gt;A script function needs an explicit contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are its inputs?&lt;/li&gt;
&lt;li&gt;What does it return?&lt;/li&gt;
&lt;li&gt;Does it modify data?&lt;/li&gt;
&lt;li&gt;Does it call an external service?&lt;/li&gt;
&lt;li&gt;What happens when it fails?&lt;/li&gt;
&lt;li&gt;Does it depend on the current user?&lt;/li&gt;
&lt;li&gt;Does it depend on the current application?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once a function is reused, its author cannot be the only person who understands how to call it. The platform should make its parameters, return value, description, and effects visible to callers.&lt;/p&gt;

&lt;p&gt;This resembles ordinary software engineering, but there is an additional constraint in a low-code platform: the people using script functions may not be full-time developers.&lt;/p&gt;

&lt;p&gt;Readability matters more than cleverness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Permission boundaries cannot depend on author discipline
&lt;/h2&gt;

&lt;p&gt;Permissions are the most sensitive part of scripting because scripts are highly flexible.&lt;/p&gt;

&lt;p&gt;Without runtime controls, scripts can bypass the platform's existing boundaries. A script may retrieve a field that the current user cannot view, modify an approved amount, or query company-wide data for a user limited to one department.&lt;/p&gt;

&lt;p&gt;The platform cannot rely on every script author to avoid these mistakes voluntarily. It must enforce the boundaries at runtime.&lt;/p&gt;

&lt;p&gt;Data reads must pass permission evaluation.&lt;/p&gt;

&lt;p&gt;Data writes must have an explicit execution identity.&lt;/p&gt;

&lt;p&gt;External calls must record their source.&lt;/p&gt;

&lt;p&gt;Scripts running as the system require stronger auditing.&lt;/p&gt;

&lt;p&gt;Scripts invoked by automation must inherit the relevant automation permission boundaries.&lt;/p&gt;

&lt;p&gt;System identity is the difficult case. Some background work genuinely needs it, including nightly synchronization, repairing historical records, and receiving status updates from external systems.&lt;/p&gt;

&lt;p&gt;But system identity cannot mean unlimited authority. It needs an authorization scope that defines which applications it can access, which tables it can read or write, which scripts it can call, and which external resources it can reach.&lt;/p&gt;

&lt;p&gt;The more capable the scripting runtime becomes, the more important these boundaries become.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging and errors are first-class capabilities
&lt;/h2&gt;

&lt;p&gt;Once scripts enter real business operations, logging is not optional.&lt;/p&gt;

&lt;p&gt;For every execution, the platform should be able to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who triggered it?&lt;/li&gt;
&lt;li&gt;Where was it triggered?&lt;/li&gt;
&lt;li&gt;Which parameters were passed?&lt;/li&gt;
&lt;li&gt;Which script version ran?&lt;/li&gt;
&lt;li&gt;How long did it take?&lt;/li&gt;
&lt;li&gt;What did it return?&lt;/li&gt;
&lt;li&gt;Did it throw an exception?&lt;/li&gt;
&lt;li&gt;What was the stack trace?&lt;/li&gt;
&lt;li&gt;Which data did it affect?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When automation invokes a script, the two execution records should be linked. An operator should be able to open the script step from an automation run and inspect its inputs, output, duration, and exception details.&lt;/p&gt;

&lt;p&gt;That is what makes scripting governable.&lt;/p&gt;

&lt;p&gt;Error handling also needs a unified model.&lt;/p&gt;

&lt;p&gt;After a script fails, should automation continue or terminate? Should the script return a structured error or throw an exception? Should the failure be shown to the user or recorded only in backend logs?&lt;/p&gt;

&lt;p&gt;These decisions should not be improvised in every script. The runtime needs consistent error behavior so callers can handle failures predictably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Script management is more than a code editor
&lt;/h2&gt;

&lt;p&gt;When people imagine script management, they often begin with an editor: syntax highlighting, a save button, and a run button.&lt;/p&gt;

&lt;p&gt;That is only the visible surface.&lt;/p&gt;

&lt;p&gt;The real challenge is managing the script lifecycle.&lt;/p&gt;

&lt;p&gt;How should scripts be organized and searched? How are they published into the runtime? Can they synchronize with a Git repository? Which automations depend on a changed function? Can a new version be rolled out gradually? Can it be rolled back? Who sees failures? Which NPM packages does a script depend on? Which platform objects does it call? Does it introduce a permission risk?&lt;/p&gt;

&lt;p&gt;Repository URL, branch, synchronization path, and authentication may sound like developer concerns. Once an application contains many scripts, however, they determine whether teams can collaborate, review changes, recover from mistakes, and synchronize environments.&lt;/p&gt;

&lt;p&gt;Scripts should not exist only as text stored in a database. They need a management model closer to software engineering.&lt;/p&gt;

&lt;p&gt;The editor is where a person writes a script. Script management governs everything from creation and dependency tracking to publishing, execution, auditing, and rollback.&lt;/p&gt;

&lt;p&gt;If the lifecycle is not managed, every additional script makes the platform harder to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What matters in the initial version
&lt;/h2&gt;

&lt;p&gt;I would not begin by building a complete browser-based IDE. I would first clarify the runtime model and governance boundaries.&lt;/p&gt;

&lt;p&gt;First, distinguish server-side and client-side scripts. Server scripts carry business logic; client scripts handle interface interactions and lightweight calculations.&lt;/p&gt;

&lt;p&gt;Second, support reusable script functions with clear inputs, return values, descriptions, and invocation contracts.&lt;/p&gt;

&lt;p&gt;Third, support code snippets inside automation so local complex calculations do not consume the entire process.&lt;/p&gt;

&lt;p&gt;Fourth, establish a unified execution context containing the current user, current application, invocation source, input parameters, and execution instance.&lt;/p&gt;

&lt;p&gt;Fifth, keep permissions restrictive by default. Data access, platform functions, and external resources must remain behind platform boundaries.&lt;/p&gt;

&lt;p&gt;Sixth, expose capabilities through allowlists. NPM packages and Java objects should not be opened without control. Begin with a small, stable surface and expand deliberately.&lt;/p&gt;

&lt;p&gt;Seventh, implement logging and a unified error model early. Every execution must be traceable across automation, listeners, buttons, and other callers.&lt;/p&gt;

&lt;p&gt;Eighth, provide version management. At minimum, the platform should record who changed a script, what changed, when it became active, and whether it can be rolled back.&lt;/p&gt;

&lt;p&gt;Only after these foundations are stable would I invest heavily in the editor experience.&lt;/p&gt;

&lt;p&gt;A beautiful editor cannot compensate for an ungoverned runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  The direction
&lt;/h2&gt;

&lt;p&gt;Scripting is not an exception inside a low-code platform. It is a necessary capability for real business complexity.&lt;/p&gt;

&lt;p&gt;But scripts must be placed correctly.&lt;/p&gt;

&lt;p&gt;They do not replace forms, workflows, automation, or the permission system.&lt;/p&gt;

&lt;p&gt;They carry complex logic that configuration cannot express clearly.&lt;/p&gt;

&lt;p&gt;Automation handles orchestration.&lt;/p&gt;

&lt;p&gt;Scripts handle calculations, validation, transformation, and limited complex processing.&lt;/p&gt;

&lt;p&gt;The platform handles permissions, logs, runtime limits, and lifecycle management.&lt;/p&gt;

&lt;p&gt;With those boundaries, scripting does not pull low-code back toward uncontrolled customization. It makes the platform capable of addressing the business cases that visual configuration alone cannot handle.&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://www.ainformat.com/" rel="noopener noreferrer"&gt;INFORMAT&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>software</category>
    </item>
    <item>
      <title>Automation Is Not a Scheduled Task. It Is Business Action Orchestration</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Sun, 09 Aug 2026 14:05:12 +0000</pubDate>
      <link>https://dev.to/informat/automation-is-not-a-scheduled-task-it-is-business-action-orchestration-1kll</link>
      <guid>https://dev.to/informat/automation-is-not-a-scheduled-task-it-is-business-action-orchestration-1kll</guid>
      <description>&lt;p&gt;In the previous post about plugin mechanisms, I made one distinction clear:&lt;/p&gt;

&lt;p&gt;Plugins provide entry points.&lt;/p&gt;

&lt;p&gt;Listeners are event entry points.&lt;/p&gt;

&lt;p&gt;APIs are external entry points.&lt;/p&gt;

&lt;p&gt;Custom controls are interface entry points.&lt;/p&gt;

&lt;p&gt;Automation should organize the business actions that follow.&lt;/p&gt;

&lt;p&gt;So this post continues with automation.&lt;/p&gt;

&lt;p&gt;In INFORMAT, automation is not simply a scheduled task. A more accurate description is a visual programming environment.&lt;/p&gt;

&lt;p&gt;It uses variables, flow control, and automation steps to organize business actions into a program that can be invoked, accept parameters, and return a result.&lt;/p&gt;

&lt;p&gt;This positioning matters. If automation is understood only as "run a task at a particular time," its role in a low-code platform is severely underestimated.&lt;/p&gt;

&lt;p&gt;Enterprise automation is more often about reliably completing a chain of actions after a business event occurs.&lt;/p&gt;

&lt;p&gt;A new record may need additional fields populated automatically.&lt;/p&gt;

&lt;p&gt;An approved process may need to generate a project.&lt;/p&gt;

&lt;p&gt;A button may open a form so the user can provide additional information before execution continues.&lt;/p&gt;

&lt;p&gt;An external system may call an automation over HTTP and receive the processing result.&lt;/p&gt;

&lt;p&gt;None of these scenarios is primarily a scheduled task. They are business action orchestration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation should behave like a small program
&lt;/h2&gt;

&lt;p&gt;When I first thought about an automation module, I did not imagine a task list.&lt;/p&gt;

&lt;p&gt;A task list can express when an action should run. Enterprise processes need much more.&lt;/p&gt;

&lt;p&gt;An automation often needs input parameters. It needs variables, conditional logic, and loops over collections. It may query or update tables, invoke scripts, and return a result. In some cases, it must pause halfway through, ask a user to complete a form, and then resume.&lt;/p&gt;

&lt;p&gt;Automation is therefore closer to a small program.&lt;/p&gt;

&lt;p&gt;The difference is that developers do not have to write the whole program from scratch. They assemble it from steps on a visual canvas.&lt;/p&gt;

&lt;p&gt;This makes the business path visible.&lt;/p&gt;

&lt;p&gt;Who triggered the automation?&lt;/p&gt;

&lt;p&gt;Which parameters were provided?&lt;/p&gt;

&lt;p&gt;Which conditional branch ran?&lt;/p&gt;

&lt;p&gt;Which steps were executed?&lt;/p&gt;

&lt;p&gt;What result was returned?&lt;/p&gt;

&lt;p&gt;When all of this is hidden inside code, troubleshooting is expensive. When it is expressed on an automation canvas, both implementation teams and developers can understand the execution path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Input parameters are the automation entry point
&lt;/h2&gt;

&lt;p&gt;An automation needs input parameters if it is going to be called from multiple places.&lt;/p&gt;

&lt;p&gt;A listener can pass the current record.&lt;/p&gt;

&lt;p&gt;A button can pass the current page context.&lt;/p&gt;

&lt;p&gt;An external system can pass request parameters through an HTTP call.&lt;/p&gt;

&lt;p&gt;Another automation can pass the result of an earlier step.&lt;/p&gt;

&lt;p&gt;Input parameters turn an automation from an isolated task into a reusable capability.&lt;/p&gt;

&lt;p&gt;Consider an automation that generates customer numbers. It may be called by a listener when a customer is created, by an import automation, or by an external customer synchronization. With a clear input contract, the same automation can serve every entry point.&lt;/p&gt;

&lt;p&gt;There is an important design detail here: the selected parameter type mainly improves expression suggestions at design time. The runtime does not strictly validate the type of every incoming value.&lt;/p&gt;

&lt;p&gt;That means an automation must treat its input carefully. It cannot always assume that every caller passed a valid value.&lt;/p&gt;

&lt;p&gt;Critical automations should begin with conditional checks or use a script to normalize parameters. Once an automation is exposed through several entry points, invalid input can otherwise make failures deep in the execution path difficult to diagnose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variables let steps hand data to each other
&lt;/h2&gt;

&lt;p&gt;Automation rarely ends after one step. It is usually a sequence.&lt;/p&gt;

&lt;p&gt;Data found by one step must be used by the next. A calculated result may need to be written back to a record. A response from an API call may determine whether processing should continue.&lt;/p&gt;

&lt;p&gt;This requires variables.&lt;/p&gt;

&lt;p&gt;Variables hold temporary state inside the automation context. They may come from input parameters, be created by a variable step, or contain the return value of another automation step.&lt;/p&gt;

&lt;p&gt;For example, an automation could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query a customer record.&lt;/li&gt;
&lt;li&gt;Store the customer's region in a variable.&lt;/li&gt;
&lt;li&gt;Select a sales team based on that region.&lt;/li&gt;
&lt;li&gt;Update the customer owner.&lt;/li&gt;
&lt;li&gt;Return the owner information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each step passes data to the next. Without variables, automation can express only simple sequential actions. With variables, it can express business logic.&lt;/p&gt;

&lt;p&gt;But variables should not accumulate without discipline. The more variables a canvas contains, the harder it becomes to read. Interactive steps also require the runtime to persist the current execution state, so excessive variables have a performance cost.&lt;/p&gt;

&lt;p&gt;I prefer to distinguish between two kinds of variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business variables that genuinely need to survive across steps&lt;/li&gt;
&lt;li&gt;Temporary variables used only for intermediate calculations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first group should have clear names. The second should be discarded after use or kept within the smallest practical scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flow control determines how far automation can go
&lt;/h2&gt;

&lt;p&gt;Without flow control, automation can only execute from top to bottom.&lt;/p&gt;

&lt;p&gt;That solves simple problems, but real business logic quickly reaches the limit.&lt;/p&gt;

&lt;p&gt;Imagine validating a purchase order. The automation first queries the order details, loops through each line item, retrieves the related material, and compares the tax-inclusive price with the material's maximum purchase price.&lt;/p&gt;

&lt;p&gt;If a condition fails, execution stops. Otherwise, the automation continues by updating status, displaying a message, and refreshing the form and table modules.&lt;/p&gt;

&lt;p&gt;This is no longer "run a task." It is a business program expressed visually.&lt;/p&gt;

&lt;p&gt;Real processes contain branches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different customer levels use different assignment rules.&lt;/li&gt;
&lt;li&gt;Different contract amounts notify different people.&lt;/li&gt;
&lt;li&gt;Different data sources require different validation.&lt;/li&gt;
&lt;li&gt;Every row in an import must be processed individually.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conditional branches and collection loops are therefore foundational automation capabilities.&lt;/p&gt;

&lt;p&gt;A condition uses an expression to control the path. When the expression evaluates to true, the matching branch runs. Otherwise, execution follows the alternative branch.&lt;/p&gt;

&lt;p&gt;A collection loop executes a group of child steps for every item in a list.&lt;/p&gt;

&lt;p&gt;These capabilities look ordinary, but they determine whether automation can perform a few simple actions or express an entire business procedure.&lt;/p&gt;

&lt;p&gt;Another essential step is terminate execution.&lt;/p&gt;

&lt;p&gt;Termination is not merely stopping. It ends the automation and rolls back the transaction after the latest blocking step.&lt;/p&gt;

&lt;p&gt;This behavior acknowledges that automation needs transaction boundaries and data consistency. Many enterprise automations modify data. If a critical validation fails, later writes must not continue.&lt;/p&gt;

&lt;p&gt;In that situation, termination is not a convenience. It is a safety mechanism.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation steps must represent real business actions
&lt;/h2&gt;

&lt;p&gt;An automation ultimately performs work through steps. Those steps need to cover the actions that enterprise applications actually use.&lt;/p&gt;

&lt;p&gt;Table steps handle querying, creating, updating, and deleting records, as well as recalculating lookup and summary fields.&lt;/p&gt;

&lt;p&gt;Control and variable steps handle loops, conditions, variable assignment, variable updates, termination, code snippets, script calls, automation calls, return values, and execution progress.&lt;/p&gt;

&lt;p&gt;Function steps handle HTTP requests, notifications, waiting, application events, client events, and messages shown inside modules.&lt;/p&gt;

&lt;p&gt;AI-related steps can invoke language models such as ChatGPT.&lt;/p&gt;

&lt;p&gt;Together, these steps make automation more than a flowchart. They make it part of the platform runtime.&lt;/p&gt;

&lt;p&gt;Consider a customer lead entering the system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query whether a duplicate customer already exists.&lt;/li&gt;
&lt;li&gt;If no duplicate exists, create the customer record.&lt;/li&gt;
&lt;li&gt;Select an owner based on region and industry.&lt;/li&gt;
&lt;li&gt;Update the customer owner.&lt;/li&gt;
&lt;li&gt;Send a notification.&lt;/li&gt;
&lt;li&gt;Invoke a script to generate follow-up suggestions.&lt;/li&gt;
&lt;li&gt;Return the processing result to the caller.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sequence combines table operations, conditions, scripts, notifications, and a return value. Writing every step as custom code is expensive. Providing them as configurable automation steps lets the platform carry a large share of everyday enterprise operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Return values make automation composable
&lt;/h2&gt;

&lt;p&gt;Many automations should not simply finish after executing their actions. They need to return a result to the caller.&lt;/p&gt;

&lt;p&gt;An application API may use the automation result as its HTTP response.&lt;/p&gt;

&lt;p&gt;An expression may invoke an automation and continue calculating with its result.&lt;/p&gt;

&lt;p&gt;Another automation may choose its next step based on the returned value.&lt;/p&gt;

&lt;p&gt;A script may also invoke an automation and consume its result.&lt;/p&gt;

&lt;p&gt;This makes the ability to set an automation return value essential. It changes automation from a background action into a composable platform capability.&lt;/p&gt;

&lt;p&gt;The model is similar to a function. A function without a return value can only perform side effects. An automation with an explicit return value can participate in larger programs.&lt;/p&gt;

&lt;p&gt;Consider a quotation automation. It accepts a customer, products, quantities, and discounts. Internally, it queries pricing rules, calculates taxes, and evaluates available promotions. Finally, it returns the quotation details.&lt;/p&gt;

&lt;p&gt;The same automation can be called from a form button, an API, another automation, or a script.&lt;/p&gt;

&lt;p&gt;It is no longer merely an action behind a button. It is a business function inside the platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interactive steps connect automation with human judgment
&lt;/h2&gt;

&lt;p&gt;Automation is usually associated with background execution, but INFORMAT also supports interactive steps.&lt;/p&gt;

&lt;p&gt;When execution reaches such a step, the system persists the current variables and asks a user to complete an interaction. It may open a custom form, for example. After the user submits it, the runtime restores the variables and continues from the paused step.&lt;/p&gt;

&lt;p&gt;This design is valuable because many enterprise processes are only partially automatic.&lt;/p&gt;

&lt;p&gt;The system may complete most of the work while still requiring a person to provide information or make a judgment in the middle.&lt;/p&gt;

&lt;p&gt;After importing a spreadsheet, the system may parse the data but ask the user to map several incomplete fields.&lt;/p&gt;

&lt;p&gt;Before generating a contract, it may retrieve the customer and products but ask the salesperson to confirm the reason for a discount.&lt;/p&gt;

&lt;p&gt;Before starting a project, it may create a draft from the contract but ask the delivery owner to confirm the start date.&lt;/p&gt;

&lt;p&gt;Pure background automation is too rigid for these cases. Turning every step into a separate page loses the continuity of the automation.&lt;/p&gt;

&lt;p&gt;Interactive steps connect the two models.&lt;/p&gt;

&lt;p&gt;Automation pauses. A user participates. After submission, automation resumes.&lt;/p&gt;

&lt;p&gt;This allows automation to carry business processes that include both machine execution and human judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP calls turn automation into a business API
&lt;/h2&gt;

&lt;p&gt;An automation can also be invoked over HTTP. Once enabled, an external system calls a dedicated endpoint, passes parameters, and receives the automation's return value.&lt;/p&gt;

&lt;p&gt;This gives automation a natural API capability.&lt;/p&gt;

&lt;p&gt;An external system does not always need direct access to a collection of low-level APIs. It can call one automation that performs input validation, data queries, business decisions, record updates, and result construction internally.&lt;/p&gt;

&lt;p&gt;The exposed interface then represents a business action instead of a scattered set of data operations.&lt;/p&gt;

&lt;p&gt;Suppose an external ERP needs to synchronize a customer status. It could call a generic "update record" API. A better option may be to call a "synchronize customer status" automation.&lt;/p&gt;

&lt;p&gt;That automation can verify that the customer exists, validate the new status, write a status-change record, update the customer table, send a notification when necessary, and return a structured result.&lt;/p&gt;

&lt;p&gt;This model is closer to the real needs of enterprise integration.&lt;/p&gt;

&lt;p&gt;An automation invoked through HTTP cannot contain interactive output steps. That restriction is reasonable. An HTTP call requires a single request and response; it cannot pause indefinitely while waiting for a user.&lt;/p&gt;

&lt;p&gt;Automations exposed as APIs must therefore distinguish synchronous processing paths from interactive business paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits are part of the design
&lt;/h2&gt;

&lt;p&gt;Unrestricted automation is easy to misuse.&lt;/p&gt;

&lt;p&gt;A loop can accidentally become infinite. Processing a large dataset can take too long. Automations can call other automations recursively until the execution path becomes impossible to control.&lt;/p&gt;

&lt;p&gt;The runtime increments a step counter each time a step runs. Once execution exceeds the configured limit, the system terminates the automation and raises an error.&lt;/p&gt;

&lt;p&gt;This may look like a technical limit, but I see it as part of platform governance.&lt;/p&gt;

&lt;p&gt;The more open a low-code platform becomes, the more clearly it needs boundaries. A single configuration error should not be able to exhaust the resources of an entire application.&lt;/p&gt;

&lt;p&gt;Large loops and complex calculations are often better implemented as code snippets. This follows the same boundary described in the previous article:&lt;/p&gt;

&lt;p&gt;Automation is for orchestration.&lt;/p&gt;

&lt;p&gt;Scripts are for complex logic.&lt;/p&gt;

&lt;p&gt;The two should not try to replace each other. Automation connects business steps. Heavy calculations, complicated loops, and data cleansing belong in scripts or code snippets so the visual canvas remains readable and execution remains manageable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What matters in the initial version
&lt;/h2&gt;

&lt;p&gt;I would not begin by pursuing an extremely sophisticated orchestration engine. I would first make the core runtime model clear.&lt;/p&gt;

&lt;p&gt;First, automation needs input parameters and return values so listeners, buttons, APIs, scripts, and other automations can call it.&lt;/p&gt;

&lt;p&gt;Second, automation needs a variable context. Steps must pass data to each other, scopes must be clear, and expressions should provide useful suggestions.&lt;/p&gt;

&lt;p&gt;Third, automation needs basic flow control. Conditions, collection loops, termination, automation calls, and script calls form the foundation for business logic.&lt;/p&gt;

&lt;p&gt;Fourth, automation needs stable business steps. Table operations, HTTP requests, notifications, logging, progress updates, and application events should come first.&lt;/p&gt;

&lt;p&gt;Fifth, automation needs interactive steps. Many enterprise processes are not purely background actions. The ability to pause, preserve variables, wait for user input, and resume is critical.&lt;/p&gt;

&lt;p&gt;Sixth, automation needs HTTP invocation so it can serve as a business interface for external systems.&lt;/p&gt;

&lt;p&gt;Seventh, automation needs runtime limits and termination controls. Step-count limits, infinite-loop protection, and execution monitoring are baseline requirements for platform stability.&lt;/p&gt;

&lt;p&gt;Once these capabilities are reliable, automation becomes more than a set of configured actions. It becomes a business runtime unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The direction
&lt;/h2&gt;

&lt;p&gt;Automation occupies a central position in a low-code platform.&lt;/p&gt;

&lt;p&gt;Forms collect data.&lt;/p&gt;

&lt;p&gt;The data model organizes it.&lt;/p&gt;

&lt;p&gt;The workflow engine handles approval and state transitions.&lt;/p&gt;

&lt;p&gt;The plugin mechanism defines extension entry points.&lt;/p&gt;

&lt;p&gt;Scripts carry complex logic.&lt;/p&gt;

&lt;p&gt;Automation determines how business actions move across all of them.&lt;/p&gt;

&lt;p&gt;It can be triggered by a listener or a button. It can be called by another automation, a script, or an external HTTP request.&lt;/p&gt;

&lt;p&gt;It can accept parameters, assign variables, evaluate conditions, process collections, invoke scripts, interact with users, and return results.&lt;/p&gt;

&lt;p&gt;That is no longer a scheduled task.&lt;/p&gt;

&lt;p&gt;It is the business action orchestration layer of a low-code platform.&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://www.ainformat.com/" rel="noopener noreferrer"&gt;INFORMAT&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why Low-Code Platforms Need a Plugin Mechanism</title>
      <dc:creator>informat</dc:creator>
      <pubDate>Sat, 08 Aug 2026 12:43:16 +0000</pubDate>
      <link>https://dev.to/informat/why-low-code-platforms-need-a-plugin-mechanism-5ag3</link>
      <guid>https://dev.to/informat/why-low-code-platforms-need-a-plugin-mechanism-5ag3</guid>
      <description>&lt;p&gt;In the previous post about workflow engines, I ended with a distinction:&lt;/p&gt;

&lt;p&gt;Workflows determine how business moves forward. Automation and plugins determine how the platform extends those actions beyond its core.&lt;/p&gt;

&lt;p&gt;This post is about the plugin mechanism.&lt;/p&gt;

&lt;p&gt;But first, the term "plugin" needs some clarification.&lt;/p&gt;

&lt;p&gt;In INFORMAT, a plugin is not a package where developers install a large block of isolated business logic. I think of it as a set of extension points.&lt;/p&gt;

&lt;p&gt;Automation and scripts still carry most of the business logic. Event listeners, APIs, and custom controls act as entry points and connection points. At the right moment, they pass events, data, and user actions into automation or scripts for processing.&lt;/p&gt;

&lt;p&gt;This distinction matters because a low-code platform should not encourage teams to scatter logic everywhere.&lt;/p&gt;

&lt;p&gt;It should provide enough entry points for business-specific extensions while keeping the actual logic inside a unified automation and scripting system.&lt;/p&gt;

&lt;h2&gt;
  
  
  A plugin should not be an isolated block of code
&lt;/h2&gt;

&lt;p&gt;When designing a plugin mechanism, it is tempting to begin with a simple idea: open up a place where developers can write code.&lt;/p&gt;

&lt;p&gt;That sounds flexible.&lt;/p&gt;

&lt;p&gt;In practice, it can make a platform difficult to control. Every customer adds a little code. Every project introduces another rule. Every page hides one more condition. Delivery may be fast in the short term, but long-term maintenance becomes painful.&lt;/p&gt;

&lt;p&gt;A sustainable plugin mechanism should answer three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When does the business need an extension?&lt;/li&gt;
&lt;li&gt;Where should that extension enter the platform?&lt;/li&gt;
&lt;li&gt;What should execute the actual business logic?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My answer is that entry points can be distributed, but logic should be centralized.&lt;/p&gt;

&lt;p&gt;Listeners can respond to data changes.&lt;/p&gt;

&lt;p&gt;APIs can expose platform capabilities to external systems.&lt;/p&gt;

&lt;p&gt;Custom controls can introduce specialized interactions into a page.&lt;/p&gt;

&lt;p&gt;But these entry points should avoid accumulating complex logic themselves. Whenever possible, they should invoke automation, scripts, or reusable platform actions.&lt;/p&gt;

&lt;p&gt;That separation prevents the platform from turning into a collection of scattered custom code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listeners are event entry points
&lt;/h2&gt;

&lt;p&gt;An event listener has a direct job: when a particular event occurs, it triggers a configured automation or invokes a script.&lt;/p&gt;

&lt;p&gt;Common events include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before a table record is created&lt;/li&gt;
&lt;li&gt;After a table record is created&lt;/li&gt;
&lt;li&gt;Before a table record is updated&lt;/li&gt;
&lt;li&gt;After a table record is updated&lt;/li&gt;
&lt;li&gt;Before a table record is deleted&lt;/li&gt;
&lt;li&gt;After a table record is deleted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These events may look basic, but they are critical extension points in enterprise systems.&lt;/p&gt;

&lt;p&gt;After a customer is created, the system may generate a customer number.&lt;/p&gt;

&lt;p&gt;Before a contract is updated, it may validate the amount against the available budget.&lt;/p&gt;

&lt;p&gt;After an order is created, it may synchronize the order with an external ERP system.&lt;/p&gt;

&lt;p&gt;Before a record is deleted, it may determine whether deletion is allowed.&lt;/p&gt;

&lt;p&gt;After data changes, it may notify the responsible team.&lt;/p&gt;

&lt;p&gt;If every requirement is implemented inside the core table logic, the platform becomes heavier with every new use case. If the platform emits events through listeners and lets automation or scripts handle the response, its core can remain stable.&lt;/p&gt;

&lt;p&gt;That is the value of a listener.&lt;/p&gt;

&lt;p&gt;It is not the business logic itself. It is the entry point that triggers the business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  APIs are entry points for external systems
&lt;/h2&gt;

&lt;p&gt;Listeners primarily handle events inside the platform. APIs connect INFORMAT with the systems around it.&lt;/p&gt;

&lt;p&gt;Enterprise digital operations never depend on a single system. A company may already use ERP, finance, MES, office automation, collaboration tools, or an internal data platform.&lt;/p&gt;

&lt;p&gt;INFORMAT should not assume that it will replace every one of them. It needs APIs so external systems and the platform can call each other.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An external system creates a customer record.&lt;/li&gt;
&lt;li&gt;A finance system writes back a payment status.&lt;/li&gt;
&lt;li&gt;A button in a collaboration tool triggers an INFORMAT workflow.&lt;/li&gt;
&lt;li&gt;An AI assistant calls a business API to retrieve authorized data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are also plugin entry points. An external system enters through an API, and INFORMAT uses automation and scripts to perform the subsequent actions.&lt;/p&gt;

&lt;p&gt;The difficult part is not merely making the request succeed. The platform must also handle permissions, logs, errors, and business state after the call.&lt;/p&gt;

&lt;p&gt;A bare API eventually produces a collection of disconnected integrations. An API connected to the automation and scripting system can bring external actions into the same runtime as internal processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom controls are interface entry points
&lt;/h2&gt;

&lt;p&gt;Standard form and page controls cover most common scenarios: text, numbers, dates, users, departments, attachments, related records, and sub-tables.&lt;/p&gt;

&lt;p&gt;Enterprise applications still encounter specialized interactions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selecting a location on a map&lt;/li&gt;
&lt;li&gt;Scanning a code to enter data&lt;/li&gt;
&lt;li&gt;Displaying an equipment status panel&lt;/li&gt;
&lt;li&gt;Editing contract clauses&lt;/li&gt;
&lt;li&gt;Building a complex quotation component&lt;/li&gt;
&lt;li&gt;Calling an external pricing service when a user clicks a button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building every possible interaction into the platform would make the core product bloated. Custom controls should therefore serve as extension points for the interface.&lt;/p&gt;

&lt;p&gt;But a custom control should not become a container for all business logic either.&lt;/p&gt;

&lt;p&gt;The control handles interaction.&lt;/p&gt;

&lt;p&gt;Automation handles business actions.&lt;/p&gt;

&lt;p&gt;Scripts handle complex calculations.&lt;/p&gt;

&lt;p&gt;APIs handle external calls.&lt;/p&gt;

&lt;p&gt;With these boundaries, a custom control does not become a black box. It collects a user action and hands that action to the platform's unified runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation is the orchestration layer
&lt;/h2&gt;

&lt;p&gt;If listeners, APIs, and custom controls are entry points, automation is the orchestration layer.&lt;/p&gt;

&lt;p&gt;It determines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a process is triggered&lt;/li&gt;
&lt;li&gt;Which conditions must be satisfied&lt;/li&gt;
&lt;li&gt;Which actions should run&lt;/li&gt;
&lt;li&gt;What happens after a failure&lt;/li&gt;
&lt;li&gt;Whether subsequent actions should continue&lt;/li&gt;
&lt;li&gt;Whether users should be notified&lt;/li&gt;
&lt;li&gt;What should be written to the execution log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider what happens after a customer record is created.&lt;/p&gt;

&lt;p&gt;A listener captures the event. Automation checks whether the customer came from the company website. If so, it assigns the customer to a salesperson, sends a notification, and creates a follow-up task. If the customer is rated as level A, it also alerts a manager.&lt;/p&gt;

&lt;p&gt;This business chain should not live inside the listener.&lt;/p&gt;

&lt;p&gt;The listener triggers it. Automation organizes it.&lt;/p&gt;

&lt;p&gt;That makes the system easier to understand and much easier to troubleshoot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scripts are the complex logic layer
&lt;/h2&gt;

&lt;p&gt;Automation is effective for orchestration, but not every kind of logic belongs in visual configuration.&lt;/p&gt;

&lt;p&gt;Some logic is inherently complex:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quotation calculations&lt;/li&gt;
&lt;li&gt;Budget validation&lt;/li&gt;
&lt;li&gt;Data cleansing&lt;/li&gt;
&lt;li&gt;Signing external API requests&lt;/li&gt;
&lt;li&gt;Evaluating complicated conditions across multiple tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Forcing all of this into configuration can make the visual editor harder to use than code. Scripts are necessary for logic that automation cannot express well.&lt;/p&gt;

&lt;p&gt;Scripts still need clear boundaries. They should run inside a context provided by the platform. A script should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify the current user and application&lt;/li&gt;
&lt;li&gt;Access the current table and record&lt;/li&gt;
&lt;li&gt;Read only permitted fields&lt;/li&gt;
&lt;li&gt;Return a structured result&lt;/li&gt;
&lt;li&gt;Be invoked by automation&lt;/li&gt;
&lt;li&gt;Produce an execution log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this model, scripts are not fragments of code hidden around the system. They become governed parts of the platform runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why these layers should remain separate
&lt;/h2&gt;

&lt;p&gt;Separating entry points, orchestration, and logic keeps the platform manageable.&lt;/p&gt;

&lt;p&gt;Complex logic inside listeners becomes difficult to maintain.&lt;/p&gt;

&lt;p&gt;Direct external calls from custom controls turn pages into business black boxes.&lt;/p&gt;

&lt;p&gt;API calls that modify core data without automation or logging become difficult to trace when something goes wrong.&lt;/p&gt;

&lt;p&gt;I prefer a structure with explicit responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Listeners handle event entry points.&lt;/li&gt;
&lt;li&gt;APIs handle external entry points.&lt;/li&gt;
&lt;li&gt;Custom controls handle interface entry points.&lt;/li&gt;
&lt;li&gt;Automation handles orchestration.&lt;/li&gt;
&lt;li&gt;Scripts handle complex logic.&lt;/li&gt;
&lt;li&gt;Permissions enforce boundaries.&lt;/li&gt;
&lt;li&gt;Logs provide traceability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these parts form a sustainable plugin mechanism for a low-code platform.&lt;/p&gt;

&lt;p&gt;This is not primarily a plugin marketplace. It is an extension architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extension mechanisms must be observable
&lt;/h2&gt;

&lt;p&gt;Once extensions enter real business operations, the greatest risk is often not missing functionality. It is being unable to find where a failure occurred.&lt;/p&gt;

&lt;p&gt;Did the listener fire?&lt;/p&gt;

&lt;p&gt;Did the automation run?&lt;/p&gt;

&lt;p&gt;Did the script fail?&lt;/p&gt;

&lt;p&gt;Did the API call time out?&lt;/p&gt;

&lt;p&gt;What parameters did the custom control send?&lt;/p&gt;

&lt;p&gt;Which fields were ultimately changed?&lt;/p&gt;

&lt;p&gt;Did the failure prevent the main process from continuing?&lt;/p&gt;

&lt;p&gt;The platform must be able to answer these questions. Every execution should leave a record that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The event source&lt;/li&gt;
&lt;li&gt;The identity that triggered it&lt;/li&gt;
&lt;li&gt;The application, table, and record involved&lt;/li&gt;
&lt;li&gt;The automation that was called&lt;/li&gt;
&lt;li&gt;The script that was executed&lt;/li&gt;
&lt;li&gt;Its input and output&lt;/li&gt;
&lt;li&gt;Execution time&lt;/li&gt;
&lt;li&gt;The reason for any failure&lt;/li&gt;
&lt;li&gt;Whether the action was retried&lt;/li&gt;
&lt;li&gt;Whether it blocked subsequent processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without these records, every increase in extensibility makes the system harder to govern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extensions must not bypass permissions
&lt;/h2&gt;

&lt;p&gt;Plugin entry points introduce another risk: they can accidentally become permission backdoors.&lt;/p&gt;

&lt;p&gt;A user may be unable to edit a field in the standard interface, while a custom control modifies it through an API.&lt;/p&gt;

&lt;p&gt;A user may only be allowed to view their own customers, while a script queries every customer.&lt;/p&gt;

&lt;p&gt;An AI assistant may call an API and retrieve data that the current user should never see.&lt;/p&gt;

&lt;p&gt;Listeners, APIs, custom controls, automation, and scripts must all remain inside the permission system.&lt;/p&gt;

&lt;p&gt;They can extend business capabilities, but they cannot bypass business boundaries.&lt;/p&gt;

&lt;p&gt;Every execution needs an explicit identity. Is it running as the current user or as the system? If it uses a system identity, what is that identity allowed to do?&lt;/p&gt;

&lt;p&gt;The platform must define which fields can be read or written, which external services can be called, and which actions require an audit record.&lt;/p&gt;

&lt;p&gt;These boundaries must be designed from the beginning. Otherwise, a mechanism that looks flexible in the short term creates significant data risk over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What matters in the initial version
&lt;/h2&gt;

&lt;p&gt;I would not begin by building a sophisticated plugin marketplace. I would first complete the internal extension path.&lt;/p&gt;

&lt;p&gt;First, listeners should support the core data events: before and after create, update, and delete.&lt;/p&gt;

&lt;p&gt;Second, listeners should be able to trigger automation. They remain entry points while automation owns the business sequence.&lt;/p&gt;

&lt;p&gt;Third, automation should be able to invoke scripts. Scripts handle special validation, complex calculations, and external integrations.&lt;/p&gt;

&lt;p&gt;Fourth, custom controls should be able to trigger platform actions. They pass user interactions to automation or scripts instead of carrying heavy logic in the frontend.&lt;/p&gt;

&lt;p&gt;Fifth, API calls should enter the unified logging system. The platform should trace their source, parameters, results, and effects.&lt;/p&gt;

&lt;p&gt;Sixth, permissions and auditing should be restrictive by default. The more powerful an extension mechanism becomes, the less reasonable it is to grant broad access automatically.&lt;/p&gt;

&lt;p&gt;Only after these foundations are reliable does a broader plugin ecosystem become meaningful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The direction
&lt;/h2&gt;

&lt;p&gt;A plugin mechanism should not turn a low-code platform back into a conventional coding platform.&lt;/p&gt;

&lt;p&gt;Its purpose is to leave room for differentiated enterprise requirements without destabilizing the platform core.&lt;/p&gt;

&lt;p&gt;For INFORMAT, the most accurate model is a set of extension points:&lt;/p&gt;

&lt;p&gt;Listeners provide event entry points.&lt;/p&gt;

&lt;p&gt;APIs provide external entry points.&lt;/p&gt;

&lt;p&gt;Custom controls provide interface entry points.&lt;/p&gt;

&lt;p&gt;Automation orchestrates business actions.&lt;/p&gt;

&lt;p&gt;Scripts carry complex business logic.&lt;/p&gt;

&lt;p&gt;Permissions and logs keep the system governable.&lt;/p&gt;

&lt;p&gt;That architecture creates space for customization while preserving a coherent runtime. For an enterprise low-code platform, that balance matters far more than simply allowing more code.&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://www.ainformat.com/" rel="noopener noreferrer"&gt;INFORMAT&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>automation</category>
      <category>backend</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
