Embedded reusability

Published on Sep 27, 2017
By Terry / Senior System Design Engineer

To reuse code for a variety of embedded products. To save time by using a piece of code that matches the functionality that is requested. To have already tested code available and save testing time. To not spend days of debugging on why a particular function does not exactly do what it states it should do. It should be a relief for most developers and project managers alike, but people (if not a company as a whole) struggle with the idea of using something they did not create: Overcoming “Not Invented Here” Syndrome.

In practice there is hardly any piece of code that matches requested functionality for the full 100%. With a bit of luck it matches 80-90%, but needs some rework to be a perfect fit. It would make sense to do just this rework instead of (re)writing the code entirely – but do developers trust the code? From day 1 in their working carrier developers are trained to fix issues and solve problems. When given a piece of code developers will try to fix its issues and solve its problems – even when there are none. Since developers are usually only presented with things that need fixing is it so hard to imagine that all developers suffer to some degree to the syndrome mentioned above?

To get developers to actually wanting to use reusable code some guidelines can be used:

  • Learn from what is done in the past.
    There probably has been some effort to create reusable code in the past. Check what has been done, what worked and what not. Talk to developers and their experiences of that effort. Knowing what they liked and what not is a good place to start.
    Bad example 1: create a huge list of requirements, review them, finetune them, review them once more and by the time there is consensus people have moved on to use some other piece of code since no project leader is willing to spend such an amount of time to realize the plans for this proposed set of code.
    Bad example 2: let a single developer code out a framework. Even with proper documentation the ideas of this framework will not get across easily and if people do not work with it regularly it will be quickly forgotten. Maintenance on this framework by other developers will be hard, as understanding the ideas behind the framework are not easily transferred.Having good consensus amongst the users, the developers which are to use the reusable code is preferred, but keep the scope small.
  • Start out small.
    By removing manual, repetitive work, people will be more inclined to use the code. Example: convenience classes of low level peripheral drivers for I2C, SPI, UART – these busses will be present on most embedded products and will need a driver anyway. By providing something that works out of the box it saves time for a developer, this can thus be spend on more high level architectural questions which will help in the adoption rate of the code. Such a piece of code can be use independent of other (existing) code, so the adoption can be gradual.
  • Make sure it works.
    The (reusable) code must work. It should be properly tested, have a working example and receive fixes for found issues. If people try the code and find it fails on their first attempt they might not even start to debug why this happens but simple discard the code and create something new altogether. Another benefit of a working example is that it showcases a best practice, it can guide developers to a more structured way of working which benefits the product as a whole. The working example also acts as an integration test, if feature changes are implemented this test can be reused as well.
  • Expand when needed.
    When new features are required the code can be expanded to include those as well. Make sure to have proper separation of the code: do not create overly complex blocks which can do everything, rather layer the functionality. This allows to leave out functionality when not needed. An example is arbiter functionality of a bus: if low level peripheral code only needs to move bytes around, another piece of code can worry about the synchronicity of the bus. This allows for smaller, more understandable code – if only a single user uses the bus the arbiter can be left out.
  • Be consistent.
    If the (reusable) code uses similar interfaces, it will be easier to understand and maintain. Within a single product adding more reusable code parts will be easier as they work in similar fashion. More products using the reusable code will start to have similar means to interact with the code, which can speed up development of these products as well.
  • Document.
    The code should have proper documentation: on the interface level and more detailed on function level as well. It helps in understanding the code, eases maintenance and gives a more “mature” feel to the code. A working example can be of use, implementing this example will highlight unclarities (which can be refactored) or hint to comments which should be added to be able to use the code correctly.
  • Have management support.
    If management does not support the idea of using (or even creating) reusable code products simply will not use it. There is always great pressure to release a product as fast as possible, meaning if reusability is not requested no time is spent in creating or updating reusable code.

Even with guidelines above and all effort put into this, some developers will not be convinced and not use the reusable code. The only way to deal with this is to have very clear agreements on code ownership – who is responsible for what part – and try to use the reusable code on the parts which fall not under his responsibility. The use of reusable code can be easily defended (if need be), at the same time it will increase code quality. When having repeated success with the reusable code, pressure on using it will be higher with every passing day. In the end, all developers strive for good working products, if something has proven its usefulness the adoption in future products will slowly become a requirement.