Embedded programming is like web development
the.scapegoat.dev
the.scapegoat.dev
https://www.philipotoole.com/a-prayer-for-distributed-system...
"The idea of monitoring the responsiveness of a read-modify-write cycle is obviously nonsense from a programmer’s point of view (though not so for the microprocessor designer!)."
For the system you describe to have the practical challenges of a distributed system it would also need experience some of the following:
- some of the "different processors" would need to fail at seemingly random times, only to come back again in a few minutes.
- the speed at which those "different processors" respond would need to vary widely, and different times of the day.
Note that I'm not saying "easier" or "harder". It is different, though.
This is a real difference, and one I want to expand on in my next article. You can fuck around much more in a web context than embedded, but you still often find out.
Doing web "well" requires thinking in terms of realtime and memory constraints. I don't want the latency of my microservice to jitter, nor do I want it to use random amounts of memory. Not only will I be able to provision smaller machines, but I will also guarantee better SLAs. Building your microservice just like you would a hard realtime constraint (guaranteed memory usage per request, preferably no allocations at all, time guarantees (response within 50 ms or I fail) helps make a robust, performant and resilient web application.
The real-time aspect and hard resource constraint are indeed the fundamental difference. Some of these (static memory allocation) make a lot of sense when building microservices, for example, but it's much more squishy. I personally like building my microservices very similarly to my embedded systems: event driven, with different priorities to ensure time constraints, and bounded statically allocated memory and queues. Even in languages like golang, my allocations are usually done up front, memory ownership is very clearly delineated.
While you might not have realtime in web systems, you very much want to avoid GC pauses and similar behaviour to avoid spiking your latency, as these things compound. The underlying concepts are a bit similar, at least in my head. I think "this needs to be O(1) in runtime and memory to be repeatable. If the constant factor is bad, we can work on that. But I don't need fast and elastic).
As for distributed system, I actually consider a SPI / i2c / CAN system to be the distributed system, and a lot of patterns (retry mailboxes, timeouts, promises, bounded queues, circuit breaker) make sense in both.
I definitely plan to write more about these lower level details, and provide some code examples to make the parallels more evident.
Still, there is a huge difference in my opinion. C and Javascript are quite different to write, test, debug and deploy. I have not a deep understanding of web development and don't care much about the latest frameworks or the latest approach to solve something. I just use it for visualization purposes, which are fairly restricted on embedded systems most of the time. Quite the contrary to web solutions.
There is a contrast to your average C spin-loop and some exaggerated JS promise chain. Also I think some parts of embedded development are easier, especially bare metal applications. Only static memory allocation for example, predictable common access to shared memory between main loop and interrupt subroutines. On the other hand you don't just execute some unit tests on your machine. You probably need specialized software. The complexity of the system as a whole is far lower than all the monkeys involved in displaying a website. The hardware to use is most often far less general than a webbrowser and you really need to read the manual and system specification of your CPU of choice.
Of course embedded systems also include the usual linux box. This kind of embedded development is far more comparable to classical application development. Getting desktop OS to do I/O beyond keyboard/mouse/display comes with its own perils. In practice these systems mostly interface lower level systems that do specific tasks while providing interfaces to networking. All in all these are pretty distinct worlds in my opinion.
Thanks for reading. Fully agree on web being both more forgiving, and ultimately more complex than embedded development.
I do think however that a C-spin loop can benefit from knowing JS promise style. One of my proudest libraries is a monadic task system based on futures, that handles cancellation, introspection, error handling and user interaction, in a static memory constrained system. It makes some more complex interactions like: - move the motor until the limit switch is hit - if the limit switch is not hit within 15 seconds, activate the fan for 5 seconds - if the motor controller fails to respond, then activate the fan but show a red LED - if at any point the temperature sensor goes above 30C, interrupt the motor moves by slowing down the motor first - if at any point the user cancels, turn on the fan before slowing down the motor to a stop
This makes for a mad state machine, but is actually not that complex in a task kind of write up.
I plan to write much more about this topic, including the fact that embedded is a lot of "library" work (managing datasheets and different architectures and sensor versions and register locations), while web work can be a lot of "keeping up with the joneses" (in terms of UX, frameworks, cloud tech, etc...).
Do you have that library published somewhere?
On some very rough level significant similarities exist. But I think it is much rather the case that a certain kind of web development is like a certain kind of embedded development. Web development is a very wide field and so is embedded development. And for very many parts they are very different. If you are writing your own OS and you have your own scheduler, carefully saving registers before jumping to the new stack pointer, you are doing something no web developer would ever do.
The systems are similar on an abstract level, but in the realities of the details they heavily diverge. And this is not very surprising, I think. Afterall the only reason software is useful is because allows abstraction, which allows for generalization.
Good points, I wonder if at this point I'm just saying "software development is software development!"
I do think there are a lot of similarities in building the run-of-the-mill "sensors + actors + orchestrator + UI" embedded system, deploying and monitoring it. Which I must admit is most of the system I have built over the years, some had an expansive IoT/Connectivity component, some of them didn't.
After handling the low-level peripherals, I find that a lot of time is spent on say, properly handing UI workflows, be it in react or with an ad-hoc push-button / encoder / character LCD setup. Or say, doing a RPC + streaming data exchange over USB. The point I'm trying to make, and that I might expand on in more details, is that for people outside the field (mostly on the web side, of course), it might not be apparent that: "you know that USB is kind of websockets + polling HTTP?". Hopefully that leads more people to do embedded development beyond arduino and micropython (which are great, and honestly my first prototyping choice).
Another common task in embedded systems: writing a driver. Most drivers I wrote are "look up the kernel API, look up the device datasheet, write the glue", which I find very close to "look up $service1 API, lookup $service2 API, write the glue". There's subtlety on the one side in how to say, deal with priorities and deferred tasks and userland locking, but that subtlety is very much present on the other side too, with retries, idempotency, latency, event loss, etc... To me again, a lot of similarities, and I will use the same kind of techniques (mocking so I can mostly develop in userland / localhost, instrumenting tricks, etc...)
Writing an OS (or, in general, building the underlying frameworks and tooling, like say writing a database engine) is a pretty rare thing.
There is also a large amount of crossover as embedded gains more resources. Often you're working with javascript because your device needs to have an embedded web server with custom functionality. Sometimes you see SQlite on embedded flash, although debatable if they really needed it. Running Linux is far more common and it works pretty much the same regardless if it's on an MCU or a VM in the cloud.
The main difference I see is to do with the ease of fixing mistakes in the field. With web development I can usually git push and the issue is fixed for all users. With embedded, I almost never have an internet connection so it's a case of physically sending people to site or product recall to fix the issue if it's serious. Internet connections are becoming more common in embedded so we can remote update, but still the minority in general.
Lots of common ground, but just like any discipline it's that last 20% that makes the difference.
I think embedded system engineers should be more open to learn from other programming domain and try to catch the good things to apply it in the embedded world. For sure there is some specific constrainsts in the embedded world limiting the usage of web tools. But I agree with you at a system point of view there is a lot of similarities and we could solve a lot of problems by applying the same strategies.
I'm an embedded system engineer specialist on robotics. Each robot I worked on have multiple boards (my personal record is 35 boards). Scaling a system like that is an absolute nightmare. That's why I'm now working on an open-source microservice orchestrator dedicated to critical real-time embedded systems. The concept is to follow the metodologies used to scale complex web applications thanks to a tool able to deal with embedded system specificities. You can find my Luos lib here : https://github.com/Luos-io/luos_engine Let me know what you think about it.
On the higher levels of embedded Linux there can be a grain of truth in this (especially wrt asynchronous model).
But the lower levels of embedded (both on bare metal MCUs) and on the lower level parts of embedded Linux require hardware skills without a web equivalent. While embedded software developers aren't normally expected to actually design boards they do have to be able read a schematic and review it concerning the software aspects (eg are the components used already well supported in software, are the right debug ports available etc). And quite often you have to debug with an osciloscape or logic analyser. Basic soldering to build cables etc is also very useful.
Also a lot of embedded requires understanding and modifying large codebases (eg the Linux kernel) whereas in web development its more about using existing libraries and frameworks more or less as is.
web is the default entry point for the vast majority of developers these days, and has a much more welcoming leaning curve. Of course it will have a less-hardcore reputation. Look at the ways in which even the developers specializing in webapps attempt to distinguish themselves among the crowd.
You are right that this doesn't say much (at least for experience embedded developers).
The follow-up points I want to make:
- there is a lot of value for embedded developers to get acquainted with what is going on in the web world. Not only is there a lot out there that's directly applicable to iot, but the tooling is miles ahead of what I encounter in the embedded world, and a lot can just be scaffolded out.
- there is a lot that carries over from senior web development to embedded development, and jumping from one to the other is more about "incidental" complexity (learning new tools, learning to read datasheets, learning some abstractions), but a lot of the more holistic thinking carries over. I see a lot of people scared of even opening the source code to a kernel module and I want to show that it's basically the same as glueing some S3 stuff to an SQS queue, both in complexity and in boringness.
I have been working with more junior web people, and shown them that I actually prototype my embedded systems in plain javascript, or using tools like XState, just because it's easier. Once they see how my javascript can be ported almost 1:1 to a C state machine on an arduino, big lights go on.
(editing: formatting)
For those of you who have transitioned from embedded to web or another high-demand area (ML, distributed systems), how did you find that transition? And for those who've stayed in embedded, what is it about the work that keeps you from leaving?
and as a reader who worked in embedded for a decade,
my instantaneous reaction to this headline was "JFC NO!" prior to even clicking o see if it is e.g. about domain priors or what.
Half in rejection of the contention that the actual ecosystem is similar (it's not),
mostly in rejection of that idea that it should be ("never again").