Monday, November 9, 2009

The Top Ten: Strategies for Surviving the Shifts

The Top Ten: Strategies for Surviving the Shifts
Al Stevens
Al shares his Top Ten list for how to stay in demand in the shifting sands of technology and employment.

Al is a Dr. Dobb's Journal contributing editor and can be contacted at astevens@ddj.com. Here you stand, diploma in one hand, help wanted ads in the other, ready to take on the world. Are you ready? How well has your education and hard work prepared you for what the job market expects from you? How closely do your expectations match reality? Let's examine what's in store for programmers over the next several years.

Programmers entering the workplace face challenges that earlier generations did not. There was a time when programmers could carefully master a technology and depend on that mastery for years. Those times are gone. Today, the rate of technological change accelerates faster than the average programmer can keep up. Consider your plight as a new graduate, about to go to work and eager to use the skills you learned in college. Between the time you enrolled and the day you graduated, the tools and languages of choice have changed several times over.

The class of '97 is a good example of this point. When you were freshmen in the Fall of '93, things were different. C++ had, in the course of one short year, become the language of choice for serious desktop applications development. The inside cover in Dr. Dobb's Journal of September 1993 was a two-page ad for Microsoft Visual C++. Fifteen full-page ads were for C++ related products. Five of them were for PC C++ compilers. C++ and visual programming for desktop applications dominated the attention of the press and were about to dominate the job marketplace.

Now, four years later, things have changed. The July 1997 issue paints a different picture. The inside cover ad is for Microsoft Visual Tools. The only mention of C++ is in the footnote list of several languages that Visual Tools supports. The number of C++-related full-page ads is down to seven. Fifteen full-page ads are about Internet and Java products. The Internet and Java reign in this issue as they do everywhere that trendy developer topics are discussed. The Internet, hardly mentioned four years ago, has permeated our society-not just technosociety, but mainstream society, too. There are even television programs dedicated to the Internet.

Not that C++ is dead in 1997. Help-wanted ads for C++ programmers are plentiful. But if you look back four years before 1993, you'll see a pattern. The class of '93 entered college expecting to become C programmers when they graduated. Four years is a generation in programming. Development technologies often fail to survive the length of time it takes for a programmer to learn them well. These certainly are interesting times.

Colleges and universities cannot maintain pace. Their computer-science curricula are designed and built to endure for years. Instructors cannot support a full-time class schedule and, at the same time, keep up with accelerated changes in technology and trends.

Technical managers are similarly at a loss. Management, like teaching, is a full-time job. The typical manager's hands-on technical experience is of obsolete technology. They now have to make decisions about tools and techniques without the fundamental understanding of the technical issues needed to make such decisions.

I am reminded of the comparison of the advance of technology to that of man's ability to travel. This analogy was first shown to me 30 years ago by a technical manager at NASA. He drew a two-dimensional graph on the blackboard. The horizontal dimension was time measured in decades from the dawn of homo sapien to the present. The vertical dimension was speed measured in miles per hour. (The graph was not to scale.) He drew a long horizontal line showing human speed limited to foot travel for thousands of years-only a couple of miles an hour. When man harnessed the horse and camel, the line rose a few miles an hour and leveled out for many more centuries. Tall ships in the age of exploration added several knots and much endurance and distance. The industrial age gave us the steamboat and the railroad, and the automobile was close behind. The early 20th century brought the airplane at from 100 mph to over 500 mph at midcentury. The jet airplane's speed is measured relative to the speed of sound. Space travel is measured in thousands of miles per hour. We went from foot speed through horse speed in millennia, and the line stayed fairly flat. The sharp ascent began with machines about 150 years ago; since then, we've gone from about 10-20 mph to a Space Shuttle that travels at 18,000 mph in a sharply ascending curve.

We could draw similar curves based on calculations per second or communications transactions per second. Our ability to communicate over greater and greater distances can be similarly plotted. Our ability to disseminate and assimilate information can be, too.

Growth in technology fosters greater growth in technology, at exponential rates. That growth necessitates change both in the technology and in how we deal with it. Rapid change creates unique problems for technical education and management of technology. For all those millennia, while mankind's rate of development in travel and in calculations and communications remained stable, managers and teachers had only to look back a few years to gain from the experiences of their ancestors. When today's teacher, engineer, or manager faces a problem and looks back for a solution, sometimes only a few months, they are looking at solutions that happened way down the curve. Often, the old solutions don't fit because today's problems are enmeshed in technologies that only recently came into existence. We live and work in an era of rapidly growing and shifting paradigms. The problems are not the paradigms. The problems are in predicting and keeping pace with the growth and changes.

The industry has not fully realized it yet, but we need a major overhaul in how we manage technical projects. Technical management authority must be delegated to the technicians because that's where the knowledge is. Shared leadership, wherein individuals take turns at facilitating team objectives, is one approach. We need to assign responsibility for technical decisions to participating technicians.

Education needs an overhaul, too. Educational institutions cannot hope to consistently equip students with the precise tools and techniques that they will use in the workplace. Schools should not even try to do that. To attempt to do so is futile.

How can you prepare yourself for an era in which things threaten to change faster than you can assimilate them? One approach, the one I recommend, is to learn the basics of your craft. Realize that by the time you are ready to go to work, yesterday's bleeding edge will be replaced by something new and unanticipated. Those kinds of changes will continue throughout your career, and the rate of change will accelerate. The best foundation for a good programmer is a solid knowledge of those things that underpin every development technology, no matter how different or how advanced. Understand the fundamentals irrespective of the language and the development platform.

I've put together my personal Top Ten list of technical things that every programmer needs to understand. If you have a rudimentary knowledge of these ten subjects, you are prepared to work anywhere with any programming language on any platform. All you have to do is learn the language and the platform. No one expects a new graduate to have a mastery of these items out of the chute, but plan your strategy to learn as much as you can about these concepts as you work. Your education does not stop with commencement exercises; it begins there. This list is your new course schedule for on-the-job self-help training. Each item (except one) includes a reading list that identifies one or more books that teach the lesson. Some of them are no longer in print, but a good technical library should have them. There are many other such books. Never stop reading about your craft.

My Top Ten list starts with the data, proceeds through the program, continues with the user, and ends with some personal objectives. But do not infer a priority from that order. Each of these items is equally important to a successful and fulfilling programming career.

Strategy #1: Data Structures

Data structures are containers that contain objects of data types. There are several common data structures, and each has its own behavior and application. Understand all of them, how they behave, and when you would use them. Common data structures are: arrays of one or more dimensions (also called vectors when there is only one dimension), stacks, linked lists (singly and doubly linked), bags, queues, trees (balanced, binary, red-black, and so on). There are others.

The notion of an object container might be foreign to the beginning programmer. Start, however, with the simple array that most programming languages support. You organize objects of a common type into arrays because you need to retrieve, process, and display them in their order and, perhaps, in random order based on their position in the container. Print all the employees in the order of their seniority; get me the third employee from that list, for one example; get me that employee's last annual salary adjustment, for another. An array supports the efficient retrieval of items based on their position in the array by using a subscript. A multidimensional array consists of nested arrays of arrays. The calendar is a two-dimensional array. Twelve arrays of days organized into one array of months. Inserting objects into the middle of an array is not efficient because the object elements are contiguous and must be shifted to make room for the new object.

A stack is a container from which you can retrieve only one specific object, and that object is always the most recent one added to the container, which is why a retrieval is called "popping" the stack. A list is a container that you can efficiently traverse in one or both directions as you would an array. The objects are not contiguous, though, and retrieval based on position in the container are not efficient. Insertion of an object in proximity to another object is efficient, however, since the objects are linked and not contiguous.

Understand all the common container data structures and know when and how to use them. It is also important to know how to implement these containers; not all development environments include libraries that support them. Every now and then you face a problem that calls for a specialized data structure-a container not found among the common ones. Understanding the implementation of the known containers helps you to build that occasional unique or hybrid data structure.

Fundamentals of Data Structures, Horowitz and Sahni, Computer Science Press Inc., 1976.

Data Structures from Arrays to Priority Queues, Wayne Amsbury, Wadsworth, 1985.

Strategy #2: Data Abstraction and Encapsulation

Data abstraction is the design of a user-defined data type and is a property of object-oriented programming. A user-defined data type is a type that supports your program but that the programming language does not include as an intrinsic type. C++ supports various integer data types, but it does not support an intrinsic calendar date data type. Yet many applications use dates in common ways.

Encapsulation is the process wherein you combine a user-defined type's data representation and its behavior into one encapsulated entity that has an implementation and an interface. Encapsulation is the second property of object-oriented programming. A type's representation consists of the objects that combine to represent the data values of an object of the type. A complex number consists of two floating-point numbers, the real and imaginary parts of the complex number. A date might consist of three integers, one each for day, month, and year. Or it might consist of one integer value that is the number of days since some date long past. The type's implementation consists of the data members and functions that implement the type. Its interface defines how a user of the type-the programmer whose program declares objects of the type-instantiates and manipulates the type.

Data abstraction and encapsulation are two of the four properties of object-oriented programming. The other two are inheritance and polymorphism. You can and should apply data abstraction and encapsulation irrespective of the language and the programming model. These principles can be applied in any programming language whether the language encourages them or not. Some languages, such as traditional Cobol, go out of their way to separate the data from the algorithms, but with such languages, you can use data abstraction and encapsulation by practicing careful design and using program comments and code organization.

I do not include inheritance and polymorphism in my list because not all languages support them. I do, however, encourage everyone to understand and use all the object-oriented concepts wherever possible.

An Introduction to Object-Oriented Programming and C++, Wiener and Pinson, Addison-Wesley, 1988.

Inside the C++ Object Model, Stanley B. Lippman, Addison-Wesley, 1996.

Objects In Action, Commercial Applications of Object-Oriented Technologies, Harmon and Taylor, Addison-Wesley, 1993.

Object-Oriented Design with Applications, Grady Booch, Benjamin/Cummings, 1991.

Strategy #3: Database Organization

Designing and using objects is one thing. Maintaining large numbers of objects in a database is quite another. Traditional database technology defines three basic models: hierarchical, network, and relational databases. Most contemporary database projects use some form of the relational database.

Sound database design involves a process called normalization, which tries to eliminate redundancy and inefficient data paths. As important as normalization is, it is more important to arrive at intuitive aggregates of data elements in an intuitive organization of data files. Normalization has to do with structure. Intuition is a human element. It is possible to have a database design that has achieved perfect third normal form but that is so unwieldy and nonintuitive as to be incomprehensible and unusable.

As a programmer you should understand database design even if you never design a database. Certainly you will be using databases that others have designed, and understanding the underlying design helps you use the database appropriately. A database consists of files that support a common problem domain. The files in a relational database resemble tables of rows and columns. They might remind you of spreadsheets. Each row is a database record. Each column is a data element. Each file has one key data element that uniquely identifies the records. Every employee has an employee number, for example. Retrievals are based on that key element. When a file includes a data element that is the key data element for another file, those two files are said to have a many-to-one relationship. There are many employee records for each department record. Each employee record includes a department number data element, which is the key data element for the department file. You can retrieve department information for an employee, and you can build lists of employees assigned to a department, and so on.

Computer Data-Base Organization, James Martin, Prentice Hall, 1977.

Principles of Database Systems, Jeffrey D. Ullman, Computer Science Press Inc., 1980.

An Introduction to Database Systems, Second Edition, C.J. Date, Addison-Wesley, 1977.

Object Databases, The Essentials, Mary E.S. Loomis, Addison-Wesley, 1995.

Strategy #4: Program Structure

Learn proper program structure. One fundamental principle embodies the disciplines of both structured and object-oriented programming and originated with the concept of modular programming: Design small, functionally strong, loosely coupled program modules. That is,

Design small modules. A small program component is easier to read, understand, and debug. The fewer lines of code that you have to deal with, the easier it is to assimilate what the code is supposed to do and what it is doing, which may not be the same thing.

Design functionally strong modules. Each program module should have one, and only one, clearly understood purpose. Do not write subroutines that do two functionally distinct things, even when those two things are related and always done in tandem. Write two subroutines and call them both. There are several advantages to this guideline. First, repairs to one subroutine are less likely to affect the other. Second, elimination or replacement of one procedure does not affect the other. Third, when you are looking at one, you do not have to be mindful of the other. Fourth, a bug in one is not a bug in the other; and so on.

Design loosely coupled modules. Modules are coupled by the data that they process. When a subroutine views and modifies the copy of a data structure that its caller owns, the two modules are more tightly coupled than they have to be. Each module in a tightly coupled design has the potential to affect the operation of the other. As a rule, pass copies of individual data fields to subroutines and let them work with their own private copies. To change values from procedures that you call, code them as functions that return a new field. This advice is only a guideline. There will be many times when you cannot follow it. The more often you can, the more loosely coupled your modules will be, and the stronger your program will be.

Reliable Software Through Composite Design, Glenford J. Meyers, Van Nostrand Reinhold Company, 1975.

Strategy #5: Algorithms

Learn the fundamental algorithms for processing data. Understand recursion and its behavior and application. Know how to sort and search data structures-Quicksort, bubble sort, binary search, and so on. Understand random number generators, data compression, data encryption, digital communications, expression evaluation. Learn structured query languages (SQL, for example) and know how they work and what they do. Understand graphical algorithms, including the trigonometry that supports them. Learn how and when to optimize code. Learn file-transfer protocols such as ftp and xmodem. Understand inverted database indexes; numerical algorithms; text processing; imagery, and on and on. The backbone of a computer application is its data; algorithms are its brain.

The Art of Computer Programming, Vols 1-3, Donald Knuth, Addison-Wesley, 1973.

The Data Compression Book, Mark Nelson, M&T Books, 1991.

Applied Cryptography, Bruce Schneier, John Wiley & Sons, 1994.

A Guide to the SQL Standard, C.J. Date, Addison-Wesley Publishing, 1987.

Programming Principles in Computer Graphics, Leendert Ammeraal, John Wiley & Sons, 1986.

Zen of Code Optimization, Michael Abrash, Coriolis Group Books, 1994.

Inner Loops, Rick Booth, Addison-Wesley, 1997.

Practical Algorithms for Programmers, Andrew Binstock and John Rex, Addison-Wesley, 1995.

Strategy #6: Debugging

Writing the program is only the beginning. After that, you have to get it running, which means debugging your code. A good programmer knows how to debug a program. It's something that you learn with experience.

Debugging is the act of finding bugs and fixing them. Fixing a bug is not a big deal. Finding it is the trick. Every program of any consequence will have bugs. We spend most of our time correcting mistakes. Mistakes are natural.

Debugging, however, has some of the elements of black art. The programmer looks at a malfunctioning program and wonders what to do. The solution often comes in mysterious ways.

Sometimes you just know. Other times it takes a well-organized, methodical trace of the code to get even an idea of what is wrong. Sometimes you stumble across it by accident. Other times it comes to you in a flash of inspiration, usually when you are doing something else.

There will be times when a bug hides itself too well. You decide that the bug is insidious and too stealthy for you to find. You make this determination late one night after a long, tiring, unproductive debugging session.

Get some sleep. Do anything other than work on that program. Take a walk. Sit on the beach. Ride your bicycle. Go to the movies. Do something to make you laugh and feel good. Do not look at the computer or think about the program until you are fresh and rested.

Grab a coworker. Explain the program and the symptoms of the bug. Let the programmer watch while you step through the program with the debugger, explaining at each step what you are looking at and why. As you verbalize the problem, you begin to see aspects of it that you did not see by yourself. The other programmer usually does not need to do much more than nod and say "Hmm" from time to time.

Bugs are errors in programs. Bugs can be caused by typographical errors, logic errors, exceptions that the program does not test, data that the program does not validate, memory references that are out of bounds, and numerous other causes. A bug hangs up the system, provides incorrect output, contaminates the database, or any combination of the above.

You can prevent bugs by understanding the requirements, writing reliable code, and testing as you go along. You should maintain versions of your program that work so that you have backup positions. Heed the warnings that the compiler generates. Use tried and proven libraries and tools. Test all the exceptions that the system can report.

Writing Solid Code, Steve Maguire, Microsoft Press, 1993.

No Bugs, David Thielen, Addison-Wesley, 1992.

Strategy #7: User Interfaces

Most contemporary programming assignments involve the user interface, which is the command and data entry architecture that a user uses to run the program. A major part of any interactive program is the user interface. Windows programmers have an edge. A well-defined specification of conventions is in place, both in books and programmers' guides and by example in existing programs. You have plenty of precedence to follow. Even so, it is possible to build programs that have poorly conceived user interfaces, even within the mainstream guidelines for a Windows program. Become well-versed in the issues of user interface. Better still, involve your user in the development of the user interface through an iterative process of prototype development.

Separate the design of the problem domain algorithms from that of the user interface. Make it easy to change and rearrange menu commands and dialog boxes to suit the user without compromising the integrity of how the program processes data into information.

(This item does not include a reading list. The few books I have seen on user interfaces are mostly obsolete because the paradigms change so rapidly. The trend has always been toward common user interfaces, but the styles practiced in recent years are changing due to the influence of online requirements levied by Internet-aware applications.)

Strategy #8: User Requirements

Understand the user's requirements for the program. Understand as much about the problem domain as possible in order to deliver a program that supports that domain. That understanding should be clear, unambiguous, and shared between you and the user. It should be written down.

Consider this scenario. The program is finished, tested, and delivered for the user's first test. The user reports back almost immediately. The conversation starts with this oft-repeated phrase, "I always thought it was going..."

If, in response, you refer to a concise, unambiguous written and signed statement of the program's requirements, you can negotiate the problem out of the bug-crisis arena and into the enhancement-schedule arena. With a clear requirements specification, your chances are better that you and the user both understand the requirements up front, and this scenario will not be played out.

There are two kinds of requirements: functional requirements and performance requirements.

Functional requirements say what the program is going to do. They specify the areas of the user's concerns that the program supports. They identify the data that the program maintains and the information that the program provides. They specify reporting and processing cycles and the input that the user provides. They identify aspects of the program that are critical to the user's responsibilities.

Performance requirements quantify the program's behavior. They specify database capacities, maximum storage units for each type of record, minimum acceptable response times, frequencies of processes, backup and recovery requirements, and anything else that you can enumerate.

A requirements specification contains language that users understand. It identifies the problem to be solved, not the solution. It does not include specifications of the computer, operating system, database management system, or other technical tool even when you know these things in advance. They are part of the solution, which comes later. Each requirement is a stand-alone, unambiguous statement written without technical jargon.

Later you can translate approved requirements into solutions. For example, the requirement, "Print all the employees in the order of their seniority," implies an ordering based on date hired, which implies the ability to compare and sort based on a date data type, and so on.

Structured Requirements Definitions, Ken Orr, Ken Orr and Associates Inc., 1981.

Strategy #9: Computer Architecture

Understand the hardware, not necessarily at the electronic level, but know about its memory components, cache, processor speeds, peripherals. A lot of this knowledge has found its way into mainstream understanding even among the nontechnical users, mainly due to the popularity of desktop computers. You hear computer buzzwords at every party and in every public gathering. A lot of what people think they know about computers is wrong or incomplete. Learn as much as you can about the hardware so that you can properly assess its capacity and limitations.

Understand operating system architecture, too. Know the difference between the implications of preemptive and nonpreemptive multitasking operating systems. Learn how remote procedure calls work and why that architecture is important. Understand parallel processing and concurrency.

Learn at least one assembly language well enough to write a complete program in it. Assembly language programming is a dying art, and the programming profession is that much the less for the lack of this basic skill among its practitioners. The skill translates into an understanding of computer architecture that you can't get any other way.

A computer has limitations. Know and accept those limitations. Push the envelope only when you understand the envelope.

Programmers are commonly disposed to agree to develop programs that they can intellectually comprehend. They forget to ask if the program is possible, necessary, feasible, or appropriate. Programmers, being people, place a high value on the approval of other people, particularly their managers, and they want to cooperate. Become a better judge of what the computer can and cannot do than those who ask for programs to be written.

The Indispensable PC Hardware Book, Hans-Peter Messmer, Addison-Wesley, 1994.

Mastering Serial Communications, Peter W. Gofton, Sybex Inc., 1986.

Programmer's Guide to the EGA and VGA Cards, Second Edition, Richard F. Ferraro, Addison-Wesley, 1990.

PC System Programming for Developers, Michael Tischer, Abacus, 1989.

Strategy #10: Communication

An old chestnut says that from data, we build information, which we turn into knowledge, from which we form judgment, which eventually leads to wisdom. Many people fail to realize that the computer takes us only through the first transformation. A computer processes data into information and a programmer facilitates that transformation. The rest of the evolution is in the province of people-consumers of information-as they exchange ideas and philosophies evolved from the information.

It's one thing to gain wisdom. It's quite another to pass it on. Learn to communicate verbally and in writing. Effective communication enables you to promote your ideas, explain your work, persuade others to pitch in when you need help, and sell yourself when you want an assignment or a new job. The programmer who can communicate effectively with peers, customers, and management is far more valuable to an organization than the genius who hides in a dark room, stays up all night, eats pizza, drinks caffeine beverages by the gallon, and cranks out mountains of code.

Remember, too, that communication is a bidirectional pipe. You have an input channel. Ask questions and listen. Whenever you hear a technical expression that you do not completely understand, ask what it means. Don't worry about appearing stupid. Ignorance is the reason for learning. If the answer is insufficient, do some private research and find out what they are talking about. An informed, articulate programmer is always paid the respect due any expert.

You are a part of a large society, and your success in that role depends on your ability to exchange ideas. Take a course in public speaking. Read books on effective written communications; any decent public library has scores of such books. Read anything you can whenever you can. Learn to use visual presentation tools. Know what and when to communicate.

The Writer's Art, James J. Kilpatrick, Andrews, McMeel & Parker, 1984.

On Writing Well, William Zinsser, Harper & Row, 1985.

The Elements of Style, Second Edition, Wiliam Strunk, Jr. and E.B. White, Macmillan Publishing, 1959.

Microsoft Press Computer Dictionary, Joanne Woodcock, senior contributor, Microsoft Press, 1991.

The Eleventh Strategy

This might be more important than anything in the Top Ten list. Pay close attention because this is one of those times when data (the experiences of my generation) begets wisdom (what I am about to impart). To paraphrase the cliche: Nobody ever said on their deathbed, "I wish I'd spent more time writing programs." It's okay to immerse yourself in your work sometimes, but not always and never to the exclusion of other things that bring quality to life. Programming is like playing music, writing, painting, and all compulsive creative activities; allow it to take over your life and it will.

In an old story, Daniel Webster defends the man who sold his soul to the Devil. In another, Ebenezer Scrooge realizes late in life the futility of a devotion to business that excludes the concerns of mankind. Both fictional men are related to that programmer who does nothing but grind out code. Employers love those nerdy programmers, but check out what the managers, bean counters, and stockholders are doing on those weekends when you are chained to the keyboard trying to serve their purposes. They are with their families, enjoying what the world has to offer. Do the same.

DDJ

No comments: