The 7 Habits of Highly Dysfunctional Developers
How to destroy a project. Originally in Embedded Systems Programming, July, 1998.
For novel ideas about building embedded systems (both hardware and firmware), join the 27,000+ engineers who subscribe to The Embedded Muse, a free biweekly newsletter. The Muse has no hype and no vendor PR. It takes just a few seconds (just enter your email, which is shared with absolutely no one) to subscribe.
By Jack Ganssle
Embedded systems are typically so complex, with so many interrelated components, each of which must be perfect, that practically anyone can do an very effective job of botching a development project.
Still, it's instructive to examine some of the habits of the most defective engineers, some of whom have honed dysfunctional development to a high art.
It's important to understand the dynamics of embedded systems: promise the world, start writing code, and let the project fall completely apart. There's no penalty for non-performance! As the deadlines draw near, and then pass by, and then fade away as old forgotten memories, your employer will have so much vested into you and the project there's no chance you'll be disciplined, no matter what bizarre work habits you display.
In fact, a few carefully placed comments about greener pastures may result in winning a bonus from your panicked employer!
So, here are a few ways of maximizing your job security through proper dysfunctional design and management of your next embedded project.
1 - Overpromise
Make promises you know you can't keep. This cardinal rule is most effective when applied to scheduling. It's notoriously difficult to create a schedule that is accurate, so don't worry, be happy, wing it.
One perfect indicator of an impossible schedule is that sick feeling one gets in the gut when moving Pert chart triangles around in an effort to meet marketing's arbitrary deadline.
When you find yourself fudging data to meet expectations you know you're on track to mastering the art of overpromising. When you whisper lots of "maybes", "I hopes", and "ohmygods"; when miracles are expected and required, then your overpromising skills are truly in the upper percentiles of the defective.
The most defective development groups have perfected this to a fine art, at times even bursting into loud guffaws when management requests a schedule or a progress update.
Never limit your overpromising practice to schedules! Embedded systems are singularly subject to the merits of overpromising on system performance. Always promise that a 1 MHz 8051 will suffice. Always promise data transfer rates that press the state of the art.
2 - Defer Hardware Issues
Sometimes the most frustrating part of getting your code to work is making it interface to the system's hardware. There's no sense in getting upset at the beginning of a project when you know that the end will be a disaster anyway, so always defer dealing with hardware requirements until you've written many thousands of lines of hardware-dependent code.
Though modern C compilers are quite efficient even at handling low level tasks like talking to the hardware, proper convoluted design requires writing all drivers in assembly language. Minimize documentation and maximize obfuscation to enhance your job security.
The hardware engineers will surely be happy to respin the board design as often as required, especially if the changes are requested within a week of final delivery.
When it's time to assign blame, remember that modern FPGA and PLD design means the hardware is now just like the software - it's never done.
3 - Ignore Real Time
Never address real time issues until the night before delivery. Deferring performance problems, in particular, will assist in following rule 2 (Defer Hardware Issues), particularly if your code is so slow you'll have to replace the 8051 main CPU with a Pentium-class machine.
Some of the most successful defective developers have managed to create systems without an RTOS, and then convince their bosses to shoehorn the RTOS in only at the last minute.
Be sure to convince your boss that no commercial RTOS is quite right for your application. Writing your own will fill many a lonely evening. Customers will delight in finding the subtle bugs sure to be left behind in the final executable.
Since an application that is properly segmented into multiple tasks differs so much from the standard stream of consciousness approach, shoehorning an RTOS into your system late in the game will potentially add years to the project and to your job security.
4 - Glorify Globals
Use lots of global variables. Every module should be dependent on variables from every other module.
Make use of today's powerful tools which impose no penalty for the use of huge include files. Define every variable within the include file, so you won't have to worry about remembering which one is used where.
It doesn't hurt to use cryptic naming conventions as well, to obfuscate the origin and influence of each global. Since the monster include will define variables with the most limited scope, yea even unto loop counters, remember that standard practice calls for expanding the old FORTRAN practice of naming temps. Use the names "i", "ii", and (my personal favorite) "iii", just appending "i"s as needed to create unique labels.
Ignore the precepts of modern object oriented software design, which requires that variables be encapsulated within the functions that use them. Anyone who has done a complete OOP embedded project realizes just how much thought and planning is required to do so correctly.
Take the easy approach now, since you know you'll be debugging forever anyway. Remember that "maintenance" is nothing more than an abstract concept touted by overpaid programming gurus, since most software never makes it to the field anyway.
A frequently overlooked benefit of globals is their profound influence on your personal job security. Creating alarming numbers of dependencies in your code surely leads to the company being forever dependent on your skills.
5 - Discourage Understanding
Software is complex. Few of us will ever understand a chunk of code over 50k lines. Bear in mind that software consultants tell us we should keep functions under a page as that's all the average programmer can comprehend.
Since the history of the industry shows us that most software folks just start writing the code before bothering with a detailed design, it's clear that functions will always exceed a page. Sometimes the length of a single function may rival War and Peace. Given this fact, it seems unlikely that we'll ever understand the code anyway, so just go ahead and crank out C. As fast as possible.
Besides, since the firmware will surely take longer than expected to complete, it's best to get started immediately.
Most embedded systems run into trouble during debugging. One common problem includes synchronizing with external hardware. The wise practitioner will include a delay routine, whose input parameter is a delay time in arbitrary units. Sprinkle calls to the delay liberally into all hardware-centric activities. Tune the delay parameter until the unit works (working is the only measure of success).
UNIX programmers are intimately familiar with the concept of a "magic number". We should extend it to all aspects of embedded code. A particularly appropriate place for magic numbers is in the programming of complex peripheral chips. Be sure, though, to add comments as required. For example:
outp(0x3ab2, 0x33); /* program port */ outp(0x44ff, 0x87); /* set other port */ outp(0x9fde, 0x01); /* this seems to make it work??!! */
Modern debugging tools promote our lack of understanding of deep problems. Once, when an edit-compile-debug cycle took hours, programmers were careful to be sure they had considered all possibilities before fixing a bug. This is no longer required. As soon as a problem surfaces, change something - anything - and recompile. It'll take but a few seconds, and with luck the change just might have the desired effect.
Tuning the code with magic numbers and poorly thought out bug fixes enhances your career prospects. Quit and become a consultant to your old company, and enjoy many years of bountiful income from additional tuning and hacking.
6 - Neglect Standards
But all are boring. All take time away from the thrill of coding, the excitement of debugging, and the roller-coaster-like terror of watching deadlines flash by.
The Software Engineering Institute defines the Capability Maturity Model (CMM), which gives five levels of software maturity, as follows:
5. Optimizing - Characterized by Continuous Improvement. The organization has quantitative feedback systems in place to identify process weaknesses and strengthen them pro-actively. Project teams analyze defects to determine their causes; software processes are evaluated and updated to prevent known types of defects from recurring.
4. Managed - Predictable. Detailed software process and product quality metrics establish the quantitative evaluation foundation. Meaningful variations in process performance can be distinguished from random noise, and trends in process and product qualities can be predicted.
3. Defined - Standard and Consistent. Processes for management and engineering are documented, standardized, and integrated into a standard software process for the organization. All projects use an approved, tailored version of the organization's standard software process for developing software.
2. Repeatable- Intuitive. Basic project management processes are established to track cost, schedule, and functionality. Planning and managing new products is based on experience with similar projects.
1. Initial - Ad hoc and Chaotic. Few processes are defined, and success depends more on individual heroic efforts than on following a process and using a synergistic team effort.
Clearly, though, level 1 is far too high of a standard for the most defective development projects. Fortunately Captain Tom Schorsch of the U.S. Air Force realized that the CMM is just a subset of the true universe of development models. He discovered the CIMM - Capability Immaturity Model - which adds four levels from 0 to -3:
0. Negligent - Indifference. Failure to allow successful development process to succeed. All problems are perceived to be technical problems. Managerial and quality assurance activities are deemed to be overhead and superfluous to the task of software development process.
-1. Obstructive - Counter Productive. Counterproductive processes are imposed. Processes are rigidly defined and adherence to the form is stressed. Ritualistic ceremonies abound. Collective management precludes assigning responsibility.
-2. Contemptuous - Arrogance. Disregard for good software engineering institutionalized. Complete schism between software development activities and software process improvement activities. Complete lack of a training program.
-3. Undermining - Sabotage. Total neglect of own charter, conscious discrediting of peer organizations software process improvement efforts. Rewarding failure and poor performance.
Strive for level -3 compliance to maximize project duration and thus your own job security.
7 - Abandon Discipline
Abandon all discipline when the end of the project draws near.
If, perhaps though mistaken non-observance of the preceding rules, you happen to have employed software standards, or code reviews, or a scientific design methodology, be sure that as soon as the boss cranks up the pressure you drop all pretense of careful engineering. Just crank that code!
After all, we know that pilots, when late in pushing back from the gate, always decide to skip the take-off check list!