← Back to context

Comment by screye

7 days ago

Isn't this the problem with all no-code / low-code platforms ?

Code is merely the leanest human-readable representation for loss-less specification of requirements.

We're seeing this same pattern with 'K8s YAMLs' and 'prompt engineering'. There is an entire industry that's re-inventing new DSLs which inevitably converge to a scripting language as requirements get more complex.

Instead of reinventing the abstraction, I'd like to see no-code UX patterns that losslessly map onto the underlying abstraction. That way you can use the UX pattern until it gets too tedious, and occasionally dip in-and-out of the code-view in a non-jarring manner.

Graph UI for manipulating git trees (gitgraph) is a great example. Orchestration UI views (Airflow / Langraph) are another example that's getting there. At a higher level of abstraction, Notion (CRDT UIs) do a good job of representing collaboration-locks using blocks. At the highest view, I'm a big fan of how Gather-town represents remote collaboration.

I'd like to see more of this.

From my viewpoint the benefit of low-code is that the average business application is a matter of people filling out forms. If you want to radically lower development time you have to solve all the problems

https://worrydream.com/refs/Brooks_1986_-_No_Silver_Bullet.p...

For instance, a 2025 no code platform is likely to have "one click" deployment of a cloud application. That's great if you can accept that but if you require "on prem" this is either disqualifying or requires you to build an environment on prem that can host whatever it is they generate, which could be more trouble than building an application the old fashioned way in an environment you already know how to run.

A conventional analysis is that you should be able to generate a CRUD application out of a schema: you need a little bit more than something that looks at a SQL schema and creates a form to INSERT, UPDATE or DELETE, but that's a start.

One problem is that people write schemas that suck despite there being a body of "commonsense knowledge" about how to the world works. I had a year when I fixed a tremendous number of broken applications and one insight I got out of that was that whether it was a student who went from being an applicant to being enrolled to being an alumni or a pallet that would live in a logistics network and then enter a "reverse logistics" network and be inspected and possibly repaired to be returned to the logistics network the same data model of an object going through a number of states with state transitions was general and much better than the 15+ date columns that were added haphazardly to those columns.

Another example that kept turning up was that the ticket "1 phone number for a customer is not enough, support 2 phone numbers" is inevitably followed by "2 phone numbers is not enough, support 3 phone numbers" -- it saves time to work the proof by induction up front and just support N phone numbers up front. But talk that way and people think you're a lunatic like Doug Lenat.

Deployment, code generation from schemas, and schema generation from meta-schemas are part of the solution but if you can't solve 100% now getting that last bit is ultra-hard mode because all the essential complexity hidden by the framework is suddenly in your face.

  • > Another example that kept turning up was that the ticket "1 phone number for a customer is not enough, support 2 phone numbers" is inevitably followed by "2 phone numbers is not enough, support 3 phone numbers"

    Zero. One. Infinity.

    https://en.m.wikipedia.org/wiki/Zero_one_infinity_rule

    YAGNI, handle a singular case, and once you have multiple treat it like a list.

    • In the case of the phone number or email it is just so frickin' predictable what will happen.

      Adding multiple non-list fields is seductive because going to the list is a "structural instability" that requires using different coding techniques.

      Right now I really like https://www.react-hook-form.com/ for forms that have list values. Back in the day there were a set of techniques that seem forgotten (like how the Egyptians built the pyramids) that some developers knew and others didn't.

      It isn't hard at all to make a no-JS form where you had an "add" button next to a list of values. When you click on that button the POST data contains everything in the form as well as the key/value pair for that particular button (and not other buttons) so the back end can tell which button was pressed and redraw the form with another field. Delete can be handled the same way. If your form is not junked up with 50MB of trackers and ads and metadata for every social platform it is really fast, it was really fast even on dialup.

      With a handful of helper functions and a "router" that can display different forms depending on what you enter you can make powerful no-JS forms but that knowledge didn't quite reach 100% penetration before Angular came along, but developers who came on after that never learned it.

      1 reply →

  • Change management, designing schemas for being amenable to change management, having systems that can migrate data with an understanding of what historical context in which that data got into the system (was it imported from a source with a nuance that was fine before you changed the form, but now makes less sense with the updated meaning and positioning of the field?)... all of this is what makes software hard, whether low-code or high-code!

    One of the ironic things, to bring it back to Figma, is that giving designers and stakeholders Figma in all its glory becomes a justification for having engineers on the project, because you can't realize those exciting design visions with just Airtable and the like. Those engineers aren't useful because they can write code; they're useful because they'll (hopefully!) think through those change management and schema design considerations, building something that will be maintainable in the future. It's a good thing to have a design tool that incentivizes a level of foresight before launching a product that's meant to be best-in-class.

  • >> an object going through a number of states with state transitions was general and much better than the 15+ date columns

    It amazes me that anyone would write a schema with a dozen date columns, but I've seen such things. Once you're dealing with a large database of something like that, it's tempting to just add one more column rather than pitch why you should refactor the whole thing.

    I try to keep my schemas relational as possible. Actions and immutable records should obviously be stored in separate tables from the objects they reference. However, this does make the UX form design / CRUD process less amenable to simple solutions where forms are just generated right out of a schema.

    Your phone number example highlights this. In general of course you want to store contacts in a separate table from customers. But that means you're probably going to need a separate contacts sub-form within your customer form, rather than just inline phone fields. Then form will need to be required or inlined for any new customer before the main customer form can be saved. Stuff like that.

    Over the last 15 years or so I've built and refined my own form generator, really a DSL for designing forms that fit this type of thing. In its more basic use cases, each form lives as a row in a database, with each form_item linked to it in a separate table. The form runs a pre-fab query that expects N inputs that it binds to :variables (usually in WHERE or HAVING clauses) and then renders a pre-populated visual form with various input types like dropdowns, checkboxes, calendars, etc (based on those form_items, which can all be styled, required/not required, required based on other answers, etc). Each form_item has its own standard validation or custom validation function on both the client and server side. The form knows which table it wants to write to and which bound variable is the id field it's going to target for update. Sending it a blank id field renders the form with nothing populated and then does an insert instead of an update when it's submitted. It's very slim, about 500 LOC of Typescript and 700 LOC of PHP, including most standard kinds of validations on both ends. I've always toyed with the idea of releasing it it for people to use, but here's the rub: If you want to do anything involving writing to more than one table, you need to write a custom final function for those additional insert/updates.

    So, it's a lovely system, but someone coming to it naively would run the risk of designing schemas that were not expressive enough, to try to keep the CRUD system happy. And I think this is just an inevitable problem with all low-code solutions: They don't handle multi-dimensional data the way you want a clean schema to handle it. (And neither do users).

    • I've made automated form editors, companies I've worked for have done it. They had data nesting, with no custom functions for handling it. For example I've made a fully customisable menu editor where you could add sections, sub sections, prices to items, prices to sub items, prices for picking 2 out of X items in the menu. All sorts of crazy (optional) nesting. All saved in different tables. Took me like 2 weeks. I felt very clever at the time. Frontend was all in jQuery too!

      They all saved to relational databases with multiple tables. One form editor (which wasn't mine), allowed you to add new columns (this was in the on-prem days and back then they avoided future collisions by prefixing the custom columns with ex_).

      The problems you listed above are solveable. You don't need custom functions, you need clever property naming. Most ORMs have to solve them too.

      So nested data is not actually a problem for lo-code solutions, it's a solvable/solved problem.

      And I STILL regularly leave that sort of data denormalized. I write multiple date columns (e.g. created, lastUpdated, etc.). I still write mobile + work numbers as columns for plenty of apps.

      Why? It's simple YAGNI. For many apps it's quicker and easier to denormalize the data and keeps the code much, much simpler.

      Plenty of apps that I've seen that have been going 15+ years haven't normalized that data and it's fine.

      The problem comes when no-one refactors and keeps adding them.

      But it's extra work and it's "dangerous" work, potentially data destroying if you get it wrong.

      You get no thanks, as other devs will moan it's more complicated to use, and if you slip up you might get fired.

      So most devs take the easier and safer option and just add a new column.

> Code is merely the leanest human-readable representation for loss-less specification of requirements.

Hum... That's assuming you have a perfect development stack that hides all non-essential complexity.

And well, I have news for you, that's a low-code platform. A generic stack that hides every problem but solves every requirement just can't exist.

  • While code may no be the leanest human-readable representation, scripting languages are a quite deep local minimum. There is a difference between hiding complexity and blocking you off from complexity. Scripting languages work because when you encounter a problem your library / framework can't fix, there is at least a basic procedural Turing complete language to fall back on.

> I'd like to see no-code UX patterns that losslessly map onto the underlying abstraction. That way you can use the UX pattern until it gets too tedious, and occasionally dip in-and-out of the code-view in a non-jarring manner.

What are you describing sounds a lot like Saleforce and other CRM systems. Often the code implementation get hidden away when it's not something native in the no-code solution. Then "magic" happens and you don't quite know why until you dig into the code again.

>Code is merely the leanest human-readable representation for loss-less specification of requirements.

I think you might have meant "loss-less specification of the current design." requirements are aspirational, and signifies the intent, code is the loss-less version of how the system will actually behave.

  • No, I meant requirements.

    Stated otherwise: "Sufficiently advanced requirements are indistinguishable from code".

    Abstracted requirements define outcomes. Fine-grained requirements define code.

    • I respectfully disagree.

      The customer has given a requirement that states the variable <y> has to be calculated as y= x +1.

      However, the code that exists has been implemented such that <y> is calculated as y= x - 1.

      y=x-1 is how the current system behaves. However it conflicts with the actual requirement, which is y=x+1.

      1 reply →