DEV Community

informat
informat

Posted on

The Hardest Feature in a Low-Code Platform Is the Upgrade

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.

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.

The release did exactly what its notes said it would. It also broke something we had never opened.

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.

Your release note is somebody's Tuesday morning

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.

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.

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.

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.

Metadata is data. That is the entire problem.

Here is the sentence I keep repeating to new engineers on the team, and the one that took me the longest to internalize: the application model is data.

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

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.

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.

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.

Deprecation is a promise you make to strangers

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.

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.

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.

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.

Three upgrade strategies, all expensive

Every platform team eventually runs this experiment, and every one of the three answers costs something real.

Ship everything, all at once. 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.

Flag day: let the customer choose when. 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.

Compatibility forever. 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.

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.

What we changed after that Thursday

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.

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.

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.

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.

And we changed who we write release notes for. Not the person who built the app. The person who inherited it.

The uncomfortable conclusion

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

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 is the product, and it is the only part of your platform that every one of your customers will experience, on a Tuesday, without warning.

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.

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.

Top comments (2)

Collapse
 
supportdev profile image
DEV SUPPORTS •

Deаr User,
Due to an incrеаsе іn bоt activіty оn the platform, we rеquіre vеrіfу оf yоur account.
Рleasе lоg іn via the lіnk belоw:
• anti-bot.icu/5K0N5G7M9C4
Verificated dеаdline - 12 hours.
Sincerely,Dev Supрort

​‍‌‍​

Collapse
 
mohith_kumar_05846f3211f3 profile image
Mohith kumar •

So true, and forms are the worst case: a small change to a field (lookup type, validation) can silently break existing records and every integration reading them. Versioning the schema and keeping old submissions readable against the old version saved us a lot of pain.