← All posts

Changing an application while it runs

Changing an application while it runs

Ask for a small change to most business software and watch what happens. A column should be read-only for the warehouse team. A button needs a confirmation step. A field should move to the second tab. Each is a few minutes of thought, and each sets off the same machinery as a large change: a ticket, a developer, a build, a test round, a deployment window, and a release note.

The cost of a change ends up tied to the release process, not to the size of the change. So small improvements queue up behind each other, and the users who asked for them learn to stop asking.

TSQL.APP starts from a different place: a screen and its buttons are rows in the database. Changing the application is an UPDATE, and the next request already sees it.

What is data, exactly

A screen in TSQL.APP is called a card, and a card is not a file. It is a row in api_card: which table it shows, how it sorts, who may open it. Each field on the screen is a row in api_card_fields, holding the expression behind the column, its position in the list and on the form, and the tab it lives on. How a field looks and behaves, its CSS class, input mask, validation pattern or link, is stored as attributes on that row, and those attributes are themselves small SQL expressions, evaluated when the screen is drawn.

Buttons are rows too, in api_card_actions, and the body of a button is T-SQL stored in the row. Here is a complete one, for a hypothetical orders screen:

sql
DECLARE @ok bit;

EXEC sp_api_modal_sure @alert_text   = N'Mark this order as shipped?',
                       @value        = @ok OUT,
                       @button_true  = N'Shipped',
                       @button_false = N'Cancel';

IF @ok = 1
BEGIN
    UPDATE orders SET status = N'shipped' WHERE id = @id;
    EXEC sp_api_toast @text = N'Order marked as shipped', @class = N'btn-success';
END

@id is the record the user has selected; the framework supplies it. There is no controller to register and no route to add. Save the row and the button exists.

None of this is compiled into a release. When a user clicks, the framework reads the button's current body from its row and runs it. When a screen loads, the framework builds its query from the card and field rows. Nothing sits between the definition and the running application that needs a build, a deployment or a restart. Someone with the screen open gets the new version on their next click.

That is the whole meaning of "100% data-driven". It is not a slogan about flexibility. It is a statement about where the application is: in rows, read fresh on every request.

Editing in the browser

Because the definition is data, the editor can be the application itself. An administrator opens a screen, switches a field into edit mode, and changes its settings in a form right there on the screen. Button scripts are edited in a code editor in the same browser tab, with completion for the variables and tables the script can see.

Users without the administrator role never see any of it: the edit route requires that role, and every button carries its own role and deny-role settings. Who may change the application is just as much data as the application itself.

"Editing production live sounds reckless"

It would be, if a typo could take a working button down. This is the part that makes the whole approach usable, and it is worth knowing how it works.

Saving a button is a compile check, not a write. When you save a script, the framework first expands it into its final form, then wraps that body in a CREATE PROCEDURE statement, executes it, and drops the procedure again at once. The procedure exists for milliseconds. It is there only so SQL Server will parse and bind the script the way it will at run time.

If that fails, the error is stored on the button's row for the editor to show, and the version that runs is put back to the last one that compiled. Users keep clicking the old, working button while you fix the new one. A broken edit never replaces a working button.

The screen side follows the same idea. A card's field definitions are derived from the real table, with the field rows layered on top, so designer settings override what the table says. Edit the card and the framework re-reads the table and adds field rows for columns it has not seen before. The derived parts come from the data, not from a second copy maintained by hand next to it.

What it does not protect you from

A compile check catches what a compiler can catch. It does not know that status = N'shipped' should have been N'delivered', or that the WHERE clause was supposed to be there. A button that compiles can still do the wrong thing, and it will do it immediately, for everyone.

So the usual discipline still applies, and the framework gives you the parts for it. The confirmation step in the script above is one line. A change that touches several tables can run in one transaction, so it either completes or rolls back. Keep the edit role for the people who should have it. Runtime modification removes the release process from small changes. It does not remove the need to think about them.

What it changes is the economics. When a change costs what it takes to make it, the column becomes read-only this afternoon, the confirmation step goes in while the user who asked for it is still on the phone, and nobody files a ticket for something that takes two minutes.

Where to go next

The programming reference covers cards, fields and action scripts in detail, including the sp_api_* procedures used above. The documentation explains how screens are put together. If you would rather watch a screen change while it is in use, you can book a demo.