The 7 Habits of Highly Dysfunctional Developers
How to destroy a project. Originally in Embedded Systems
Programming, July, 1998.
For hints, tricks and ideas about better ways to build embedded systems, subscribe to The Embedded Muse, a free biweekly e-newsletter. No hype, just down to earth embedded talk. Click here to subscribe.
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
A quick glance through the literature shows hundreds of
ideas for improving software productivity. Code inspections, methodologies,
design reviews, and the like are the darlings of the gurus.
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!
|