Tuesday, October 11, 2016

Thinking in terms of API

Soon I'll be completing seven years of career in IT. I have worked as a software engineer, then a senior software engineer, and for last one and half year as a team leader. And during all this time, I have encountered a number of problems. I have seen code of various quality, code that looked like spaghetti,  totally unmaintained and terror to make any changes to. I have also seen well maintained, well architectured code. I have seen C, C++, .Net, Java, JavaScript, PHP, SQL Procedures and shell scripts. I think I have seen more than enough.
So while reflecting on all this and trying to apply my previous experience to my current work which is in embedded systems, I thought I need to think about API's and how to apply them to my work. Actually it shouldn't be a debate, API's are needed. To avoid mixing of various different functionalities, you need API'fied layers and clear separation between the various functions.
But from what I've seen in embedded applications, it's peoples style to liberally use globals, externs, and with a big application like a complex HVAC system, or a motor ECU, it turns into an unholy mess. You end up with a bunch of static, globals, extern functions and other bits that makes understanding and maintaining such a code a pain in the a**.
That's where API's come in. You are actually formalizing the functionality, and coding the prototypes for that module accordingly. This way you separate functionality into that module and hide all that away from other modules/functions etc.
Consider following example:
Recently I was working on a UDS client. This client communicates with UDS server on a vehicle, it creates UDS queries using UDS over CAN, and writes/reads the queries and response. Now, a quick and dirty hack would have been to hardcode the few, about 4 UDS queries and read off their responses. It would have been easy. But I thought about the utility and possible use cases for this little app. I came to the conclusion that there is a chance of this utility needing future functionality additions. So I tried to mudularize it. That means it got architectured into following three layers.
1. Application layer: Here we create a UDS query and send it to the next layer.
2. Service layer: Here we implement the transport layer for the UDS over CAN functionality.
3. Device layer: Here we implement a virtual CAN device and abstract its read/ write functions.
I had to think hard on the data passing between layers. Also how to maintain the logical separation. Took me about 30% more time but it was worth it. Because next week I was told to make this utility compatible with another CAN device. And with this code I just had to create another derived CANDevice class and implement the read/write functions according to this new device's API's.
Such is the use of API's for code maintainability. And such code is totally applicable to embedded applications. This would address the logical separation and readability aspects of embedded code.
The other pain aspect of embedded code is extern functins and global variables. Having application level global buffers and passing them by reference to lower layers would solve the problem of readability. It would make function prototypes a bit long but then you wouldn't have to hunt the globals around. It would also clarify data passing since all data used.in the function would be visible in its prototype.
Well, this approach does looks promising. Lets see how it works out in real life.

No comments:

Post a Comment