The last few years have seen an explosion in no-code / low-code vendors and platforms. Most of these platforms let you do one or both of the following:
- Customer-facing app development: create customer-facing web or mobile apps that people can use while greatly reducing the amount of code that needs to be written.
- Internal tool development: create internal tools that people within a company can use to read or write customer data.
To get a sense of what I mean by low-code platforms, here's a very incomplete list of tools you may have heard of: Retool, Bubble, Appian, Appsmith, Internal.io, Budibase, Tooljet, Webflow, …
When used properly, these platforms are great. They let a much broader population of non-developers build things and automate workflows. Even for people with an engineering background, they're often just simpler and faster to use than writing code from scratch.
However, low-code platforms tend to make a number of tradeoffs that limit their usefulness at scale. These problems are solvable, and the next generation of low-code platforms will be significantly more powerful as a result.
How low-code tools fall short
There are several major limitations of all of these low-code platforms:
Vendor lock-in
When you build things in most low-code platforms, it's impossible to migrate off later. At some point, you may hit a ceiling in the level of sophistication and want to build something more bespoke. At this point, you'll be forced to start over from scratch and rebuild the whole thing in pure code.
Poor support for collaboration
These platforms are well-optimized for single-player mode but become very unwieldy when used in a large software engineering team. If you're collaborating with even one other person, things like version control, code reviews, unit testing, dev/prod environments, and much more are indispensable. When using a no-code/low-code platform, you're still building software, even if you're not writing a single line of code. And if you're trying to collaborate on that software with other people, you'll find yourself needing all of these concepts from traditional software development.
Some low-code platforms, like Dynaboard, offer Figma-style real-time collaboration where multiple people can edit an app at the same time. This is a really impressive technical achievement, but it fundamentally misses the mark of how software teams prefer to collaborate. Version control is much more useful than real-time in-browser collaboration.
Difficult to debug and reason about
When you start building more complex apps in these platforms, the behavior of the app becomes increasingly difficult to think through. Instead of pure JS code that you can debug and inspect, the logic governing your UI might be hidden away as thinly disguised if
statements across dozens of menus and sub-menus throughout a low-code IDE.
Hard to keep in sync with your codebase
Low-code platforms generally require you to build apps in a silo outside of your main codebase. If this low-code app has connections back to the rest of your code, for example by hitting an API endpoint in your codebase, then it's common to see your low-code app silently fail when there's a schema change. It's often impossible to write unit tests against a low-code app.
The root cause: lack of access to code
All of these problems have the same root cause: you don't have access to the underlying code (and when they do, it's rarely useful).
Even if you're using a drag-and-drop UI to assemble your low-code app, there's still some underlying code-based representation that the platform is using to render your app. Generally, these platforms hide this from you because the underlying representation is ugly and hard to use effectively.
For example, Retool offers "source control" that lets you collaborate on Retool apps in Github. This seems great, but anything you create in Retool is represented by one or more proprietary YAML files, rather than something generally used for web UIs like React code. This makes it impossible to write unit tests or do code reviews for anything more significant than a copy change. It also means that it's impossible to port out of Retool.
An example of a platform that gets this half-right is Webflow. It's a drag-and-drop editor that you can use to make static websites without having to touch any HTML, CSS, or JavaScript. However, being able to see the underlying code is always a single click away in the Webflow UI. It's not just low-quality, table-based markup that old-school editors like Dreamweaver used to produce; it's high-quality code that you could easily port into your own codebase without any problems.
However it's still not perfect. The problem with Webflow is that it's still a silo outside of your codebase. So while you're producing high-quality, accessible code, you still can't version control it or manage it effectively. You can't use the Webflow editor against an HTML file in your own codebase. They have their own concept of versioning and backups that's nowhere near as robust as git.
The solution: better access to code
There's a simple fix which doesn't require any tradeoffs or sacrificing ease of use. The solution is for low-code platforms to allow for high-quality underlying code representations that you can take with you and store in your own codebase.
Let's revisit the above problems with most existing low-code platforms, and see how this solves each issue:
- Vendor lock-in: if everything you're building in a low-code platform is ultimately represented by normal HTML/JS/CSS/Python/etc that you have access to, you can quit any time and take it with you. You may still be bound to some vendor-specific APIs, but the vast majority of the work you've put into your low-code app will be portable and reusable.
- Team collaboration: If your low-code apps are stored as normal code in your own codebase, this allows you to use your existing developer toolchain (version control, unit tests, environments, CI/CD, etc) and thereby avoid all of the above collaboration problems.
- Debugging: A code-based representation allows you to analyze and reason about your low-code app's behavior just like any other code you would write. This is one of the nice things about using tools like Webflow, mentioned above–if a DOM element is behaving strangely in their editor, I can always just peek under the hood to see what's actually going on.
- Keeping in sync with your codebase: When you're able to store your low-code app in a repository you control, you can have a lot more connective tissue between it and the rest of your code than you would be able to otherwise. If your afraid that a schema change in your database might cause some portion of your low-code app to break, you can write unit tests to prevent against this.
There are also a lot of other advantages to this approach. The learning curve is faster since the low-code platform can leverage a your existing body of knowledge rather than having to teach a new proprietary domain-specific language. The onboarding process can also be faster if you can take existing code and port it into the platform.
An example of a platform that does this really well is Apple Xcode's SwiftUI for building native iOS and Mac apps. It has a slick drag-and-drop UI builder, but the underlying representation is high-quality code that is easy for developers to version control and collaborate on.
This is also exactly how we've built Airplane. We're a low-code platform that lets developers quickly create internal tools. Instead of a siloed app builder, Airplane lets you write code in your existing codebase, but then automates away a ton of other code that you'd normally have to write. You can add Airplane scripts (which are just normal Python or JS) into an airplane
folder in your monorepo and have it interact with the rest of your code. When you create a UI in Airplane, it's represented as React code.
The result is that you don't have to write any more code than you'd have to in any other low-code platform. But you get all the upside of writing normal code–version control, unit tests, etc. The base layer of Airplane is normal code that you own, not something that's proprietary and locked away. And we're actively working on layering tools on top of that base abstraction—over time, UI-assisted tools will make it even easier to get started and prototype changes, all the while using code-first abstractions.
Other low-code platforms can embrace the same approach without sacrificing the easy app creation experience that has made them successful. It would require treating the above problems as first-order considerations rather than things to patch over later and it will require sacrificing some amount of vendor lock-in. But the overall health of these platforms would benefit a lot from it.