Chapter 6. Construct Your Curriculum
He’d no longer be a grade-motivated person. He’d be a knowledge-motivated person. He would need no external pushing to learn. His push would come from the inside.... Motivation of this sort, once it catches hold, is a ferocious force.
We live in an age of abundant information. The invention of the printing press ushered in an era that allows even some of the poorest members of society to acquire the knowledge, and therefore the power, to change their circumstances. The ever-expanding World Wide Web and an unending series of technical innovations continue to lower the barriers to virtually any information we could ever want. As our Internet bandwidth increases and our handheld devices store a seemingly limitless amount of data, we can now access high-resolution media in text, audio, and video formats anywhere, at any time. Like any good apprentice, you will likely use the latest and greatest devices and media platforms, but there is certain information that is mainly found in plain old low-tech books. While blogs can provide an excellent stream of reading material, the vast amounts of wisdom captured in the books of experienced practitioners like Jerry Weinberg, Fred Brooks, Steve McConnell, and Kent Beck cannot be replaced, not even with higher-bandwidth information. Even if you’re not a bookworm, a successful apprenticeship needs to include some books as well as time devoted to studying. You’re not in school, though. There is no assigned reading—it’s up to you to find recommendations and construct your own curriculum.
No one can learn everything at once, but no principle or rule prevents the apprentice from learning a little of this today, a little of that tomorrow, things in some order no one ever thought of before, or learning to the point where he wants to stop and then switching to something else. He need not, when he wants to learn a certain procedure, wait until it’s time in a prearranged schedule; nor need he learn something he is not ready for, thinks uninteresting, frightening, or unnecessary. The learner makes his own curriculum.
After developing enough competence and skill to become proficient in Your First Language, you’re beginning to look around and see the incredible amount of information you still need to learn.
The number of books you need to read is increasing faster than you can read them.
Maintain a Reading List to track the books you plan to read, and remember the books you’ve read.
In the spirit of the Share What You Learn pattern, consider storing your list in a public space. This will allow other people to benefit from the things you learn. We use the wiki at http://bookshelved.org (started by Laurent Bossavit in 2002), but any public list would work just as well. Ideally, your list would allow you to sequence the books, while distinguishing which books you’ve read and when.
This pattern is not just about managing the books you plan to read. It is also a mechanism for reflecting on your past reading habits. With data spanning several years, you can start to see patterns, trends, and gaps in the things you’re choosing to study. This can help you make better decisions about what to read next. If you make this information publicly available, then there is the possibility that other people will contribute suggestions for future reading. This can help you discover hidden connections and obscure gems.
One of the most valuable things you can gain from any book is a list of other books that are worth reading. Over time, you will discover that certain books keep popping up in bibliographies, and you should move those books to the top of your reading list. Other books will drop down. Since your reading list is actually a priority queue, you will eventually realize that certain books have fallen so far in the ranking that you will probably never read them. This is fine. The purpose of this pattern is to give you a way to prioritize and filter the flood of potential knowledge.
The main difficulty with implementing this pattern is that you need a deep understanding of a topic in order to work out which books to read and in which order. One way to resolve this paradox is to initially pick books that give you a broad understanding of the topic in question, and then select books that drill down into the specific aspects that interest you. The other way to resolve this paradox is to depend on your Kindred Spirits and your mentors. Your mentors will be able to recommend must-read books, while discussion with your fellow apprentices can help you work out the order in which to read them. You can also take advantage of the public reading lists provided by other people who are implementing this pattern.
Another difficulty lies figuring out where to start. You can find an excellent list of books to populate your Reading List in Chapter 35 of Code Complete, Microsoft Press, and in the bibliography of The Pragmatic Programmer. You can also take a look at the bibliography for this book to see some of the books that inspired us.
This pattern owes a debt to Ravi Mohan’s idea of a Book Chain and to the Sequential Study pattern from Joshua Kerievsky’s pattern language for study groups. Whereas Book Chain is about asking people to recommend sets of books that will introduce you to a new topic, this pattern is more about managing the continuous stream of books you find interesting. This pattern also differs from Sequential Study because it doesn’t focus on reading books in chronological order to understand the way they influence each other. In this pattern, the book you should read next is the one that takes you can step further on your journey.
It is important to remember that this is your Reading List. It’s great to be influenced by the suggestions of others, but only you truly know your current context. Therefore, you should be the one making the choice about what to study next. That said, it’s also important to read the right book at the right time. Doing so is far more powerful than churning through a bunch of books you don’t have the experience or depth of knowledge to truly understand. Too many people read Design Patterns too early in their studies, when a book like Refactoring would be a much gentler introduction to patterns. Find Mentors and ask them for advice about which book you should read next. Timing has a powerful impact on your experience with a book.
Create a text file, perhaps putting it under source control. Type in all the books you’re currently reading. This is your Reading List, and the simplest possible implementation of this pattern. Now all you have to do is keep this text file up to date.
If you read even one good programming book every two months, roughly 35 pages a week, you’ll soon have a firm grasp on the industry and distinguish yourself from nearly everyone around you.
You have Unleashed Your Enthusiasm to open lots and lots of doors.
There seems to be an endless stream of deeper and more fundamental concepts that are eluding you, despite your proficiency at Your First Language.
Focus your thirst for learning on consuming as much of the written word as possible. Emphasize books over blogs as you construct your Reading List.
There should be seasons of The Long Road when you have (or take) the opportunity to read a significant number of books. For Dave, this was in 2002–2003, a couple years after he started programming and just as he was hitting a plateau in his first language, Perl. This season was enabled by public transportation: Dave had about 90 minutes a day on the train to read whatever he wanted. He was so intent that he would continue reading when he got off the train and walked the mile to his cubicle. Immersing yourself in the classics and primary sources of the field provides an unparalleled education when coupled with Finding Mentors and frequent interactions with Kindred Spirits.
Part of this immersion should include exploring the vast warehouse of knowledge that is the academic community. Reading the occasional research paper will stretch your mind and keep you in touch with the cutting edge of computer science, and also offers a source of challenging new ideas. Trying to implement these ideas will expand your toolbox with new algorithms, data structures, and design patterns many years before they reach the mainstream.
By reading this book, you have already begun to apply this pattern. The trick is to keep up the momentum after you’ve finished this book. Decide now what your next book will be. Buy or borrow it so that when you finish this book, you can switch immediately to the next one.
You should also try to keep a slim book with you at all times. This will let you use the little bits of dead time throughout each day (such as train journeys or waiting in queues) to learn.
Study the Classics
Joshua Kerievsky once asked Jerry Weinberg how he keeps up with all the books that come out. Jerry said, “Easy—I only read the great ones” (Refactoring to Patterns, p. 33). By Reading Constantly and Reflecting as You Work, you will, like Jerry, eventually be able to “only read the good ones.” When you pick up a book and the first thing you wonder is how out of date it is, you’re reading the wrong kind of books. Successful apprentices tend to focus on “long-lived books” and use the Web or experimentation to learn how the information has evolved. Dave remembers vividly the experience of reading his first classic in this field, The Psychology of Computer Programming, and marveling at how relevant the book felt, despite the stories of punch cards and room-sized computers. The wisdom captured in such classics is vital information to keep you heading in the right direction on The Long Road.
One danger of focusing on the classics is taking it too far and abandoning the more pragmatic knowledge and information that enables you to improve your day-to-day craftsmanship. Be sure to intermingle classics with modern, pragmatic books and/or articles in your Reading List.
What is the oldest book in your pile? Read that one first. The next time you’re flicking through another developer’s book collection, take note of the oldest books and ask the developer why she still owns them.
In practice, algorithm problems do not arise at the beginning of a large project. Rather, they typically arise as subproblems when it suddenly becomes clear that the programmer does not know how to proceed or that the current program is inadequate.
You live in a world of tight deadlines and complex software projects that use a multitude of tools. Your employers cannot afford the luxury of employing enough specialists to fill every role. You learn only enough about any tool to get today’s job done. You select a handful of tutorials on the language or library that you’re working with today. You make decisions without taking the time to understand the issues, and copy the toy examples provided with the tools. This works to the extent that you can turn your hand to anything. You acquire the ability to dive into a new technology and come up with a solution very quickly. You only ever learn the parts of a technology that you need to get your portion of the system working, and you depend on other members of the team to learn the other parts. For instance, you may be a server-side Java developer, and consequently have little or no knowledge of how the user interface was built.
You keep running into difficulty maintaining the code you’ve written because it turns out that the tutorials you followed cut corners and simplified complex issues. You find that your superficial knowledge of a thousand tools means you’re always floundering whenever a subtle bug arises or you have to do something that demands deep knowledge. People often accuse you of having a misleading CV because you don’t distinguish between a couple of weeks of extending an existing web service and a deep knowledge of the issues inherent in maintaining an interoperable and highly scalable enterprise system. What’s even worse is that because your knowledge is so superficial, you’re not even aware of how little you know until something or someone puts you to the test.
Learn to dig deep into tools, technologies, and techniques. Acquire the depths of knowledge to the point that you know why things are the way they are. Depth means understanding the forces that led to a design rather than just the details of a design. For instance, it means understanding type theory (or at least the simplification offered by the typing quadrant at http://c2.com/cgi/wiki?TypingQuadrant) rather than simply parroting the things you’ve heard others say.
As one of our ex-colleagues (Ravi Mohan, personal communication) found:
A knowledge of the various forms of concurrency (and their limits ) is more useful knowledge than “subclass Thread or implement Runnable.”
The areas where you have deep knowledge feed your confidence and guide you when you are deciding how to apply Sweep the Floor, because they indicate places where you can deliver value early in your time with a new team. More importantly, this depth of knowledge is something you can fall back on to give you the strength to tackle new areas. You can always say to yourself: “If I mastered EJBs then I can handle metaclasses.”
Another advantage of digging deep into a technology is that you can actually explain what’s going on beneath the surface of the systems you work on. In interviews, this understanding will distinguish you from other candidates who can’t describe the software they’ve helped build in a meaningful way because all they understand is one little portion. Once you’re part of a team, it’s the application of this pattern that separates out those who are making random piles of rubble (the Pragmatic Programmers called this “programming by coincidence” while Steve McConnell calls it “cargo cult software engineering”) from those who are building cathedrals.
How do we spot the cathedral builders? They are the ones on your team who end up doing the debugging, decompiling, and reverse-engineering, and who read the specification, RFC, or standards for the technologies you use. The people who do this have made a shift in perspective and built up a practiced understanding of the tools that support them.
This shift in perspective involves wanting to follow a problem down through the layers of a system and being willing to spend the time to acquire the knowledge that will make sense of it all. For instance, switching from a single core to a multicore laptop might alter the behavior of your Java concurrency tests. Some people will just shrug and accept that their tests will now behave unpredictably. Others will trace the problem down to the CPU level via the concurrency libraries, the Java Memory Model, and the specification of the physical hardware.
The tools you need to be familiar with include debuggers (like GDB, PDB, and RDB), which let you see into your running program; wire-level debuggers (like Wireshark), which let you see network traffic; and the willingness to read specifications. Being able to read specifications as well as code means that nothing is hidden from you. It gives you the ability to ask hard questions about the libraries you use, and if you don’t like the answers you get, you are capable of reimplementing them yourself or moving to a more standards-compliant implementation.
One of the ways to use this pattern is to get your information from primary sources. This means that the next time someone talks to you about Representation State Transfer, better known as REST, you should take that as an excuse to read Roy Fielding’s PhD thesis in which he defined the concept. Consider writing a blog post to clarify or share what you’ve learned, and to encourage others to read the original document as well.
Don’t just take the word of someone who is quoting a book that paraphrased an article that mentioned the Wikipedia page that links to the original IETF Request for Comment document. To truly understand any idea, you need to reconstruct the context in which it was first expressed. This lets you verify that the essence of the idea survived going through all those middlemen.
Find out who first came up with the ideas and understand the problems they were trying to solve. This kind of context usually gets lost in translation as the idea gets passed around. Sometimes you will find that seemingly new ideas were abandoned long ago, often for good reasons—but everybody has long since forgotten about it because the original context has been lost. Many times you will find that the original source of an idea is a much better teacher than the chain of people who have been selectively quoting each other for years. Whatever happens, tracing the lineage of ideas you have found helpful is an important exercise, and a habit that will serve you well through the years as you attempt to learn new things.
When you read a tutorial, you should not be looking for code to copy but for a mental structure in which to place your new knowledge. Your goal should be to understand the historical context of the concept and whether it is a special instance of something else. Ask yourself if there appears to be some underlying computer science concept behind what you are learning, and what trade-offs were made in the implementation that you are using. Armed with this deeper knowledge, you should be able to go beyond the initial tutorial when you run into problems.
For example, people often run into trouble with regular expressions because they only acquire a superficial understanding. You can get along just fine for years, maybe even decades, without really understanding the difference between a Deterministic Finite Automaton and a Nondeterministic Finite Automaton. Then one day your wiki stops working. It turns out that if your regular expression engine is implemented recursively, then on certain inputs that require backtracking it will run for a very long time and eventually throw a StackOverflowException. Ade discovered this the hard way, but luckily it happened on his toy wiki implementation and not in a production environment.
With all this focus on understanding technologies and tools in depth, you need to take care not to accidentally become a narrow specialist. The goal is to be able to acquire as much specialized knowledge as necessary to solve any problem, without losing your perspective about the relative importance of different aspects of software development.
One thing you will learn from trying to apply this pattern is that gaining deep knowledge is hard. This is why most people’s knowledge of the computer science that supports software development is a mile wide and an inch thick. It is easier and often more profitable to depend on other people’s knowledge of the fundamentals than to spend the extra effort to acquire it yourself. You can tell yourself that you can always learn it when you need it; however, when that day comes you’ll need to know everything by the end of the week, but it will take a month just to learn all the prerequisites.
Another possible consequence of possessing only surface knowledge is that you may never realize that the problem you are trying to solve either has a well-known solution or is in fact impossible (in this case, there may be a lot of academic papers on why it’s impossible and how to redefine the problem to make it soluble). If you’re only skimming the surface, then you won’t know the things you don’t know, and without understanding the bounds of your knowledge you can’t discover new things. Often the process of burrowing through all the layers of a problem will reveal an underlying concept from computer science. While the work of computer scientists can seem impractical, those who can apply the most advanced theories to real-world problems gain the ability to do things that can seem almost magical to others. Simple changes in your choice of algorithm or data structure can turn a batch job that takes months to run into something that finishes before the user has even let go of the mouse button. Someone who only knows about Lists, Sets, and HashMaps is unlikely to realize that he needs a Trie to solve his problem. Instead, he will just assume that a problem like longest-prefix matching is impossibly hard, and will either give up or ask if the feature can be deprioritized.
If you apply this pattern regularly, you will become one of those people who truly understand how their tools work. You will no longer just be gluing bits of code together and depending on other people’s magic to do the heavy lifting. Be aware that this understanding will separate you from the vast majority of programmers you work with, and will make you the logical choice for the most difficult assignments. Consequently, you will be the most likely to fail completely or succeed spectacularly. In addition, do not allow this knowledge to make you arrogant. Instead, continue to seek out opportunities to Be the Worst. Challenge yourself to assemble useful tools from these fundamental building blocks, rather than just basking in your ability to take things apart.
Find and read RFC 2616, which describes HTTP1.1, and RFC 707, which describes the state of the art in Remote Procedure Call technology as of January 1976. Armed with your deeper knowledge of HTTP, try to implement a client and a server for RFC 707. When you feel you have a good understanding of the trade-offs made by the editors of RFC 707, examine a modern open source implementation of the same ideas, such as the Apache Thrift framework that powers Facebook. Then, from your informed vantage point, write a blog post describing the evolution of our knowledge regarding remote procedure calls and distributed systems over the last three decades.
Now, go and read Steve Vinoski’s articles about RPC. Do you now have doubts about the depth of your understanding? Write a blog post about your doubts and your current level of understanding.
A rut is when you’re spinning your wheels and staying in place; the only progress you make is in digging yourself a deeper rut. A groove is different: The wheels turn and you move forward effortlessly...a rut is the consequence of sticking to tried and tested methods that don’t take into account how you or the world has changed.
Amid all this flux, something has to stay the same, or you might as well be engaged in research. How can you provide any guarantees about anything to your customer? When you say it will take a certain amount of time to deliver some feature, your customers need some basis for their confidence in your ability to deliver.
Identify and focus on a set of familiar tools. Ideally, these are the tools where you no longer need the documentation—you either know all the best practices, gotchas, and FAQs by heart, or you have written them down on your blog, wiki, or wherever you have chosen to Record What You Learn. Armed with this knowledge, you can provide reliable estimates about certain parts of your work, limiting the risk to the new and unexplored areas.
Just because these tools are familiar to you doesn’t mean you should always recommend them to others. Sometimes the best tool for the job and the one you’re most familiar with are different. At those times, you have to decide if your productivity is more important than the team’s productivity. Just because you know Struts like the back of your hand doesn’t necessarily make it easy to use.
Still, these are the tools you carry with you from project to project. They’re part of what makes you more productive than the next candidate being interviewed. If you run into problems, you already know where to go for answers. You know the problems these tools solve and the problems they cause. Consequently, you know where not to use them, which is as important as knowing where they are best applied.
Over time, you will grow more and more comfortable with this small set of tools. This has benefits, in the form of increased productivity, but it also holds dangers. If you are not careful, you may start to view your familiar tools as “golden hammers,” capable of solving any problem. There is also the danger that you may become such an expert in these tools that you are unable to let go of them, or even to recognize when better tools emerge.
The real challenge comes when you need to throw away a large part of your toolbox. Sometimes your tools will become obsolete; other times you will discover that there are better tools out there. In rare cases, your familiarity with the “state of the art” will lead you to invent something that invalidates the tools you already know.
In a time of drastic change it is the learners who inherit the future. The learned usually find themselves equipped to live in a world that no longer exists.
|--Eric Hoffer, Reflections on the Human Condition|
Ade was a very early adopter of the centralized source control system called Subversion. As it became more popular, clients would seek out Ade for their projects because of his Subversion expertise. Despite this, Ade has been tracking the emergence of a new breed of distributed source control systems from the beginning. By the time Subversion becomes obsolete, its place in Ade’s toolbox will already be occupied by Git or Mercurial. Letting go of familiar and valuable tools is a painful process, but it’s a skill that you need to acquire.
We can guarantee that the tools you use as an apprentice will be obsolete by the time you become a journeyman. In time, all of your favorite tools will become junk. For your career to prosper, you must learn to acquire and abandon familiar tools with ease. Constructing a curriculum that supports this goal is one of the challenges that all apprentices face in the transition to becoming a journeyman.
Write down a list of your familiar tools. If the list has less than five items, start hunting around for tools that will fill the gaps in your toolbox. This may simply be a matter of identifying a tool you already use but don’t know well enough, or it may involve finding new tools altogether. Either way, put together a plan for learning these tools and start implementing it today.
If you already have five familiar tools, carefully examine them. Are better and more powerful tools available? Are you clinging to tools that are already obsolete? Are there emerging tools that threaten to render elements of your toolbox obsolete? If the answer to any of these questions is yes, then start the process of replacing these tools today. If you need somewhere safe to experiment with new tools, make use of Breakable Toys.
In your formal education, you may have gotten used to having a curriculum presented to you and churning through it with little to no thought about whether these were the best books to be reading, and in the best order. You now need to become an active participant in your ongoing self-education, so that you will grow to be a developer who can wield powerful tools to organize and collect information. You may not know everything you need to know to construct your curriculum, but you have the power to synthesize the wisdom of the many people who will offer you suggestions. Learning to enjoy the learning process itself will serve you well in the ever-changing technology landscape that keeps us perpetually on our toes.
 Ade’s research on potential replacements for Subversion: http://delicious.com/ade/source-control-renaissance?setcount=100.