DOs and DO NOTs for developing Embedded Systems software

I have compiled a simple list of DOs and DO NOTs.  They should help developing solid software for small scale Embedded Systems. I also plan to write a more elaborate article or even book about this subject. It actually contains a couple of issues and ideas that I collected over the last 3 years since leaving university.


  • Design your software to use independent software modules
  • Design software interfaces to be portable and movable (e.q. allow a different module to implement it without having to change too much code)
  • Design the software to be hardware independent
  • Hide hardware definitions behind a module that can be exchanged as a whole (a module LCD and a module OLED which both implement a “Display” subsystem)
  • Document all interfaces between module
  • Typedef all structs and enums in use
  • Use meaningful status and return codes
  • Use state machines whenever useful
  • Use enum types for everything that is not a continuous measurement including status codes
  • Use structured data and organize all data that belongs together into structs
  • Limit code per function/method to fit onscreen
  • Design all software modules to use a common API and call structure (e.q. Module_Init(), Module_Cyclic() and Module_GetXYZ())
  • Document the assumed timing for a task or process
  • Use C99 types for data
  • Hide compiler specifics behind macros
  • Use the keyword static
  • Use the assert macro while developing for a SIL or unit test
  • Prototype the software independent of the actual target hardware and test it on the PC before trying it on actual hardware
  • Document every behavioural aspect of the software
  • Follow a generic code design pattern that defines nomenclature of values and functions, source code style and usage of keywords e.q. the Embedded C Coding Standard
  • Use static code checking tools like PC-Lint often
  • Separate code and data – defined default data should reside in a separate data module, e.q. device definitions


  • Mix multiple functionalities into one software module, write one module per defined functionality
  • Use ANSI C default types unless necessary (e.q. calls of the stdlib)
  • Expect the software to behave correctly, assume fault states when ever possible
  • Use global variables, provide access functions instead
  • Write for a specific microcontroller or target board – expect that the hardware might change
  • Use fix values for controlling timing, allow the user to configure any timing requirements
  • Use magic numbers, provide symbolic configuration constants instead
  • Assume that the source code is enough for documentation
  • Rely on any state and introduce failsafe states wherever possible
  • Ignore compiler warnings
  • Use master include files, only make interfaces available to a module that are necessary
  • Omit testing

Leave a Reply

Your email address will not be published. Required fields are marked *

Confirm that you are not a bot - select a man with raised hand:

Spam protection by WP Captcha-Free

This site uses Akismet to reduce spam. Learn how your comment data is processed.