Welcome to GettingStarted
This chapter provides an overview of some of them many themes covered in this book. Hopefully it will help you to both:
After reading this chapter, you can move on to the more practical, hands-on chapters that follow.
A primary goal of this book is to help intermediate programmers grow into expert developers. Beginners will also find some useful material in the early chapters. I'm not targeting expert developers in this text, but perhaps some of them might find a few sections that are useful.
Back to the index.
This text is designed to be readily comprehensible. Too many books are clever rather than clear. Any developer should be able to read this text and understand what it teaches about the art of development.
I want to show developers that there is more to programming that simply writing loops, branch statements, and calling functions. Too many developers are stuck at the intermediate level because they don't have the knowledge needed to understand expert books, strategies and algorithms. Once they understand the difference between learning the basics, and mastering proper application design, then they will be able to move forward in their careers. Even if they never become experts themselves, they will be skilled enough to work on advanced teams that develop interesting and important projects.
Engineers are resistant to change. We want to learn a technology and stick with it. But that is not what we find in the modern web development world. Change, not stasis, is the rule. We have to constantly retrain ourselves, constantly keep our finger to the wind and watch for change.
The pace of change is not going to slow down. Furthermore, web technologies are becoming more and more important, not less important.
These are obviously soft, theoretical numbers, but a geometric improvements in technology took a 10,000 years before we had writing, 1000 once we had papyrus, a hundred years once we had the printing press, and now one to two years with current technology, with the time frame shrinking.
Moore's law is the model here. Geometric improvements in technology are happening not in millennia, centuries, or even decades, but in years or sometimes even months.
Many developers with a strong creative bent lack the analytic skills necessary to help them write good code and bring their ideas to fruition. One of my goals is to help people like this succeed. If you have a creative bent, but are having trouble bringing your ideas to fruition in code, then you will find this text is designed to help you succeed.
Your discipline, creativity, business smarts, or ideas will never be worth anything if you can't bring your idea to fruition. My goal is to teach a manner of programming that people of "average" talent can use to help them accomplish their goals.
Most developers who complete beginning level courses can:
A secondary reason for covering the basics is that my target audience is intermediate, rather than advanced, developers. All intermediate developers can, by definition, benefit by a review of the basics. In fact, I think even advanced programmers often benefit from well structured review of basics.
The best programmers take pride in their code. They want to write code that works, of course, but they also want to write code that is easy to understand and easy to maintain. In particular, they want to create code that is:
Order here is important. Nearly every method or function we write should be, before everything else, testable. Once we can test it, then we can refactor it so as to make it more maintainable, reusable and robust. We also refactor our code to make it more readable. In most cases our code only needs to be fast enough not to annoy the user; there is rarely a need to make a fetish out of performance issues.
Good code has the following traits:
Good code is designed in accordance with basic programming principles such as:
Throughout this text I will try to highlight the kinds of errors in judgment that signal serious, deep rooted problems in a code base. If you look at a few samples of code from a big project, and see certain mistakes repeated over and over, then it is often fair to assume that the whole project is rife with problems. Developers often call these bad smells. We will talk about them in depth later on, but common problems include:
There are no hard and fast definitions for the kinds of tests that people write. However, I generally break tests out in two types:
Of course, it is usually the developer who runs all these tests. The difference between them is in what is being tested. Developer tests are testing code, while user tests are testing features. The person who hires you to do your work probably won't understand most developer tests, but they will understand a test that shows that a particular feature works. In fact, end users can often define what user tests need to be written, but they would not know how to define a developer test.
Developer tests are usually either:
User tests are typically either:
It is common for acceptance tests to be used as a means of proving that a program meets the specifications set forth by the person who hired out the work. "Here are tests that prove that our program does what you asked us to do."
Again, there is some disagreement between developers as to exactly how to define the various tests outlined above. For instance, I find it hard to draw a solid line between functional testing and acceptance testing in certain cases.
We will study all of the tests outlined above, but the primary focus will be on developer tests.
Refactoring is the art of improving code without adding new features or fixing bugs. Typically we refactor our code to make it more:
If we can create code that is testable, then it should be possible to refactor it so that it is maintainable, reusable and robust. The art of writing robust code is the art of learning how to test our code, how to refactor our code, how to make it reusable.
Testable code should be, by definition, reusable. It is used once in your program, and once in your tests. Thus it is reusable. If it is not easily reusable in both your tests and your program, then it needs to be refactored.
We need to know what impact a bug fix will have on the rest of our program. That is one of the reasons we test our code. After we make a bug fix, we run our tests to see what impact the change had on our code.
The art of writing code that is easy to test and easy to maintain turns out to be one of the most complex and important tasks a developer must master. Except in very rare cases, it can only be achieved through repeated refactoring. Just because your code works, or appears to work, that does not mean it is finished. As mentioned early, good code should be:
We achieve these goals by refactoring our code.
How do we learn to write maintainable, well designed code? Two key factors that help us achieve our goal are:
By programming principles I'm talking about things like:
The bottom line is that you need to have a two pronged approach to development. On the one hand you need to understand good development principles, and on the other hand you need to dig into the details of your chosen language. Good developers can move fairly easily between computer languages, but their should be one or two languages that they choose to study in depth, and on which they focus most of their energy.
So what are the programming principles mentioned in the previous section? Well, it turns out that their are many patterns and theories of development that you can study. However, in this book we will spend most of our time focused on just a few key princples:
A well designed architecture supports the Open Closed Principle:
Along with loose coupling the single-responsibility principle and TDD are core guidelines for our work. The open closed principle is one of the foundations of good software design. My classes are not really about Linux, git, Angular, jQuery, or express. They are about how to build applications using our core ideas:
Another important design principle not emphasized in this class is the Dependency Inversion Principle.
Slide decks that might be useful:
We will also spend a small amount of time studying:
As a general rule, these are the rules, ideas and guiding principles that make possible agile development:
"Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered." — Donald Knuth in "Structured Programming with Go To Statements"
We should not concern ourselves with performance issues unless we hit a specific problem with execution time. Even then, we should probably not worry about the problem until the latter stages of program development.
Developer time and project schedule are often more important than minor optimizations in noncritical code. Most users will not notice whether or not one infrequently called method is 5 nanoseconds slower than it could be after four or five hours of optimization. But everyone notices if a project is behind schedule. So don't focus on performance until the latter stages of development. Focus instead on writing clean, easy to maintain code. It will be much easier to optimize clean code than spaghetti code.
If you do have a performance problem, don't ever try to guess where it is, even if it seems obvious. Instead, use a profiler to measure performance and find the slow spots. Then review your architecture and see if there are ways to speed things up.
I've taught hundreds of students, and I have found that it is very rare for them to write code that is too slow. It is, however, common for them to write code that is buggy and overly complex. However, I have seen a number of student projects awash on the rocks because they tried to optimize a routine that probably never would have a significant impact on program performance. It is also common to see developers craft messy, hard to read code by adding optimizations that the compiler will do for you automatically. Or conversely, some attempts to optimize code prevent the compiler from performing much better optimizations.
Suppose a method that is called once runs in 100 milliseconds. Spending hours to get it run in 80 milliseconds is probably a waste of time. If you end up creating messy, hard to read, or repetitious code in the process, then you probably did more harm than good.
In general, however, it is not performance that causes intermediate level programmers to fail. Students fail to complete projects or quash bugs because they fail to properly craft their code.
It simply does not matter how fast your code is if it doesn't work and can't be tested. The first goal is to write clean, easy to maintain code that can form the building blocks for our tests and for a working program.
NOTE: I recognize that some projects, such as the core modules of an OS or compiler code generation, are very much concerned with even small performance issues. My argument here is not that there are no cases where performance is important, but only that it is generally not a high priority for typical programming assignments.
You have now heard some of the specific guidelines, goals and principles found in this book. There are, however, a few other more abstract themes that will crop up frequently.
Frankly, these probably have more to do with my personality than with any objective rules. Nevertheless, I believe that the art of writing good code involves a certain mind set. The best developers not only follow specific guidelines and principles, but also have a certain attitude toward their code and toward their fellow developers. Many developers succeed without having these personality traits, but I think they can help you make you a better developers. For instance, a great developer can be arrogant, but it is hard to be a well liked, effective team member if you are arrogant.
Arrogance stalks through the ranks of modern programmers like the plague spreading through the streets of of medieval town. Arrogance is more than simply a boorish and unattractive trait, it causes real damage. It hampers careers and in some cases destroys lives.
There is nothing more pathetic than a poor developer who loses a job because they are too arrogant to accept even simple tips on how to write better code. It's one thing to lose a job due to lack of skill, and another to lose it because you reject the principles of good application development out of sheer arrogance. The developer who is too "intelligent" to learn is a developer who will likely have a very short, or highly underpaid, career.
Here are some of the things that I hear from arrogant developers who simply don't have the sense to see how much they still need to learn:
In short, if you think everything that is unfamiliar or hard to understand is stupid, then you probably need to examine your assumptions. If you refuse to take the time to make your code fit in with your teammates code, then you are headed for trouble.
Having said this, it is not necessarily fatal for a talented developer to be narrow minded in some particular area. A classic example of such a person would be Eric Raymond, who is guilty of the OS chauvinism listed above. Of course, Eric Raymond is so talented, so hard working, and so knowledgeable that he can afford to burden himself with a few glaring flaws. The odds that you are equally talented are vanishingly small. In Raymond's case, one could even argue that he turned his weakness into a strength. His over the top support of Unix lit the fire in his belly that helped him produce some very important books.
But some developers are narrow minded not just in a few cases, but in multiple ways. In programming, as in many areas of life, it is the people who mistakenly think they know a lot who are most dangerous. In most, but not all, cases, good developers are humble developers. Arrogance is usually a sign of ignorance. Most arrogant people lack the self-knowledge necessary to see their own flaws.
An average developer who thinks they are great is a liability. An average developer who is open minded, teachable, and hard working is usually a strong asset.
If you don't need to be a genius in order to write good code, then what traits do you need? The short answer is that you need to be very well organized. If your tests, objects, modules, scripts and documents are well organized you don't need to be a genius to use them effectively.
It is not easy to get organized. In fact, it takes some special traits. Perhaps a list of such traits might look something like this:
Good developers adopt or create systems for organizing their code, their tools, and their documents. Without this ability, developers tend to waste vast amounts of time, or fail altogether. The problem is that most developers don't see how important it is to work on developing good strategies for organizing code, scripts and documents. They always want to "get on with it." The think, for instance, that adding a new feature is more important than writing a test or ensuring that an object is reusable.
Much of this book is about learning what organizational techniques are best. Whether the subject is writing tests, writing methods, writing objects, writing scripts, or storing files, we will always be looking for the best techniques and most effective way to organize our work. The theory is that these organizational skills can help us succeed.
However, even if you picked a terrible technique for organizing your code, and stuck with it, you might have a chance of succeeding. Even poor organization is much better than no organization. However, most good developers have a tendency to develop relatively efficient ways to organize their code and tools.
Because we live in a consumer society, many Americans have a tendency to accumulate a lot of junk that is not really needed. This is fine until it comes time to clean up the house. It can be very difficult to create an orderly, neat home if it is filled with an endless array of consumer products.
So the day comes when we have to start throwing away, giving away, or selling the things that clutter our house. For most people, this is a difficult process. It takes very little creativity to find justifications for keeping even broken things that we haven't used in years. But eventually, the light must dawn, and we start to winnow. The best winnowers learn that having a few things that work, and that can be found, is much better than having hundreds of things, only some of which work, and most of which can't be found without a long search.
I'm sure you see the analogy I'm setting up. I'm sure you can also come up with counter arguments. There are some programs that do just about everything and do it well. My suggestion, however, is that you adopt the clean house analogy to your programs: It is much better to write a small, simple program than a large complex program. If you really need additional features, consider creating a new program, or at least a completely pluggable module, that encapsulates the new functionality.
Almost all computer languages provide simple mechanisms for calling one program from another program. Most operating, systems, and especially Unix based systems, are designed to allow multiple programs to work in concert. The practical thing to do is create only small, simple programs that do only a few things.
Many times I have started writing a program, only to find myself bogged down in details that threatened to overwhelm me. To fight the chaos I began to strip layers of complexity. At times that meant I had to limit the features in my program, but frequently these features were not nearly as important as they were costly, or they could find a home in a second program. In the end, I ended up with a shorter, simpler program than the one I initially meant to write. But my program worked and my users liked it.
I was much better off with a robust program that worked, than with a flaky program that was cool but not reliable. And when users looked at my code, they rarely complained. It was simple but robust. It is hard to criticize code that works!
A lot of good developers have missed their chance to create a useful or important program because they have added too many features or focused too much on writing "clever" code that is fast and small, but which is also hard to debug, maintain and understand. Instead of focusing on speed and size, focus on the following concepts, and others that will be developed throughout this text.
Write code that:
If you think you have found a better way to do something, then please write me and let me know. I try to keep my mind open to suggestions, and certainly I have learned a great deal from developers who work at all levels. It's not always experts who have the best suggestions.
Depending on the quality of the comment, and my understanding of the subject, I generally have one of three responses to reader comments:
Here I include references to third part tools, books, or materials.
If you are writing the kind of simple program needed to follow along with this text, however, I think it is simplest to use the following tools:
This might also be of some help:
Key Sources of Free ebooks:
And here are the a few "home pages" and tools for web development:
More Free Books:
Also, if you end up in the Microsoft world, read and listen to everything that these two people say: