You should own the full source code for your next SCADA or HMI application
- HMI
- SCADA
- OPC UA
- Go
- React

You should own the full source code for your next SCADA or HMI application.
I say that as someone who did the opposite first.
I recommended the other way on this blog
In April 2025 I wrote Web-based HMI development for machines, recommending Smart HMI's WebIQ. I liked the drag-and-drop designer, I liked that the project files were plain text and dropped straight into Git, and I liked the built-in OPC UA client. I meant every word of it.
Then I built a real system on it — a plant data acquisition application reading a few hundred tags off a PLC over OPC UA, drawing a live mimic and trending everything at 1 Hz — and ran it in production for fifteen months.
Two of those three things I praised turned out to be narrower than I thought.
The number that changed my mind
The application I delivered contained 187 lines of JavaScript, across four files. That was the whole of what I had actually written. Beside those four files sat 3.37 MB of vendor framework I could not read, could not change, and could not run without a licence key.
Everything in between was JSON emitted by the designer. Even the stylesheet was generated.
So yes — the project was plain text and it did go into Git. But what was in version control was a record of which knobs I had turned. The thing doing the work was not in there, and never would be.
That ratio is not a detail. It decides what you are able to promise a customer.
What the ceiling actually costs
The customer asked to see two decimal places on a trend cursor instead of ten. I had to write back that it was not a customizable option. It was never fixed, because the widget was not mine.
The number of tags was a licence line. The number of people who could have the page open at once was a licence line. The designer I needed in order to edit the application at all was a separate licence line again — and during one production incident I did not have one, so I could not touch my own application while it was misbehaving.
And as the historian grew into the hundreds of gigabytes, the failure modes stopped being things I could reason about. I had freezes and crashes, and the only instruments I had were the ones the black box chose to expose.
None of that is a criticism of the vendor's engineering. It is simply what a licensed runtime is. The EULA says so plainly: the software "is not sold and does not become the property of the licensee". You own the JSON you configured. You never own the thing that renders it.
I also said, in that 2025 post, that this approach gave you "testable, maintainable code". The application I shipped had no tests. Not because I skipped them — because there was nothing to point them at.
Rebuilding it
Before I could replace the application, I had to work out what it was. That took a 1,003-line reverse-engineering report: reading the designer's own JSON, the recorder's database schema, and a minified bundle, because the manual sits behind a customer login.
You do not reverse-engineer software you own.
The replacement is one Go binary with a React app embedded in it. It went in over five days, against more than six weeks for the original.
Owning the source paid for itself almost immediately, in ways the old stack structurally could not:
- The tag ceiling went away. The server would not serve more than about 500 monitored items to a single session, and inside a closed runtime there is nothing you can do about that. The replacement shards its subscription across as many OPC UA sessions as it takes. At 810 tags that is three, each connecting, health-checking and reconnecting on its own.
- The storage problem became mine to solve. Instead of a row per sample, history is stored as one compressed chunk per tag per hour on a fixed 1 Hz grid — so no timestamps are stored at all.
- The deploy is three files. One executable, one tag file, one data directory. No runtime to install, no database server, no licence key, nothing to activate.
It ships with 142 tests that run without a PLC or a browser attached. That matters more than it sounds, and it is the direct answer to the promise I could not keep the first time.
What I am not claiming
That WebIQ is badly built, or that configured HMIs are always the wrong answer. For a small fixed scope on a supported target, a toolbox will beat hand-written code on time to first screen, every time. That is what I was responding to in 2025, and it is still true.
The claim is narrower than that. The moment your application has to do something the widget author did not anticipate — a number format, an axis scale, a subscription shape, a storage strategy — a configured project leaves you with no move to make. You file a request and you wait. Owning the source turns that into an afternoon.
Ask for the source. If the answer is that you cannot have it, price that in.
