The field of software engineering is vast. The amount of knowledge that can prove useful for any given problem is immense. And the paths to attaining enlightenment are numerous. Looking up at this towering mountain of knowledge and deciding how to scale it can be more than a little bit overwhelming. Whether you've recently begun the journey with only the basics behind you, or you've been trekking along for decades with a deep store of accumulated knowledge, you still have an incredible amount of stuff that you could learn ahead of you.
That is the beauty of software engineering. There is never a shortage of new and challenging things to learn, and that new-found knowledge can be applied to interesting problems. Sometimes you won't even know that a particular field of study holds the solution to your problem until you've cracked it open and explored its depths.
It can be useful to have a broad overview of all of the subjects that contribute to software engineering, to have some idea of what could be beneficial to study next. I'll attempt to split things up into coherent categories, but the progression of these categories does not imply a sequential order in which these subjects should be learned. In fact, I don't even claim that all of these things must be learned to be a successful programmer, or that this set of topics is complete. Such is the nature of the constantly changing field of software engineering. This set of topics couldn't possibly be either necessary or sufficient, but it should give a good idea of overall scope. With that in mind, let's survey the landscape of this mountain of knowledge we call software engineering, starting with the foundation.
Surprisingly, the basic knowledge for learning to program well does not come from learning programming languages or theory. It comes, like so many things, from the fundamentals of reading, writing, and mathematics that we all learn as children. They provide the foundation on which to build all the other programming knowledge, both directly and indirectly.
Reading is an integral part of learning everything about programming, from textbooks to technical manuals to documentation, but learning to read well provides deeper and more valuable skills. Knowing how to understand and follow instructions, how to analyze and think critically, and how to research topics that you don't understand are all important skills in programming that improve as your reading ability increases.
Writing is also an invaluable skill for programming. You will not only be writing code, but also documentation, requirements in one form or another, and more informal reports and explanations of software features, bugs, static, planning, etc. Writing is crucial because a lot of communication is done through the written word, and the better you can write, the better you will be understood. You will also be better able to express your ideas in writing, and at a deeper level, that is exactly what you are doing when writing code that will be executed by a compiler or interpreter, but read and understood by fellow programmers.
Mathematics is most directly applicable to programming in the form of algebra, and for certain types of programming, geometry. I think a lot of people mistakenly leave the connection between programming and mathematics at this shallow level and either enthusiastically agree with it or vehemently oppose it, depending on how good they feel they are at math. But the connection runs much deeper than whether programs tend to look like algebraic equations. Learning mathematics will teach you logic, reasoning, abstract thinking, and problem solving skills that will be incredibly useful in programming. After all, a large part of programming is solving problems in a logical way as efficiently as possible.
Beyond the basic mathematics of algebra and geometry, many other fields of mathematics can prove quite useful for programmers. Probability and Statistics play an important role in all kinds of data analysis as well as helping you understand your users in the aggregate. Numerical Methods will provide plenty of ways to do complex calculations, approximations, and estimations on a computer with attention to both accuracy and efficiency. Graph Theory is probably the most important higher level mathematics field because so many problems in programming can be understood and solved with graphs. And Discrete Mathematics encompasses a whole set of topics that are directly applicable to programming, including Logic Theory, Number Theory, Set Theory, Algorithmic Complexity Analysis, and Finite-State Automata.
Then we start transitioning from more Mathematics-centric topics to more Computer Science-centric topics. DSP (Digital Signal Processing) deals with the handling and analysis of time-domain and frequency-domain data. Control Systems deals with how to provide stimulus to some target based on feedback measured from that target in order to make it do something useful. Neural Networks deals with how to setup a system of small interconnected memory units that you can then train to respond in a certain way to different kinds of stimuli.
Now we're getting firmly into Computer Science territory, and that means I should bring up Data Structures and Algorithms - the basic programming tools that every programmer should know. You won't get far as a programmer without knowing about arrays and hashes, or understanding sorting and searching. Even though the basics are required knowledge, these topics go way beyond that, and there is a wealth of knowledge for programmers of every level here.
The more advanced topics of Artificial Intelligence and Machine Learning round out the set of Mathematics and Computer Science topics that are useful for a programmer to know, but are not directly about writing programs. Before we do get to programming itself, there is one more area to examine.
Under The Hood
Every good craftsman understands his tools, and programming is no different. A great programmer will have a deep knowledge of the central tool of his trade - the processor executing the code. Having a working knowledge of computer architecture will give you great insights into how code is actually executed on the metal. Microprocessor designers have all kinds of tricks that they use in a valiant attempt to make your code run faster: pipelining, branch prediction, out-of-order execution, register renaming, caching, trace buffers, prefetching, virtual memory, and many others.
You could also go even deeper into digital logic design and semiconductor devices, but that would be purely to satisfy your own curiosity. At that depth, you'll be pretty far removed from code execution. However, you should learn the language of the processor, or at least the closest reasonable thing to it - assembly language. Knowing some assembly language will give you a greater appreciation for what code actually looks like when it's executed by a processor, and it might even help you fix some especially elusive bugs if you find yourself in a situation where you don't have the source code and have to step through assembly instead.
While the processor is critically important, it doesn't live in a vacuum. The surrounding system is also important to understand. The memory hierarchy, disk subsystems, and peripheral buses all play a role in programming, and even though programming languages do their best to abstract these things away, knowing that they exist and how they impact performance will help you become a better programmer.
Now that multi-core processors are pretty much standard, it is also becoming more important than ever to understand parallel processor systems and concurrency. Part of that involves learning about memory models, and even though they quickly become mind-numbingly complex, you should at least be aware of the basics for parallel programming.
Other topics that make up the infrastructure of programming include a lot of intricate software that lies between the processor and high level software applications. This software includes the operating system, compilers and interpreters, databases, and networking. These are all deep subjects, and all worth knowing better than you do.
After all of that, we finally get to actually programming. You'll need to learn at least one programming language, of course. The more languages you learn, the better you'll understand the world of programming. To become more effective, you'll also learn other libraries, especially the standard library for your programming language of choice, as well as frameworks for building applications on different platforms. You will likely also pick up knowledge on APIs (Application Programming Interfaces) for various software services available on the internet.
To be a more efficient programmer, you'll need to become proficient in a number of other tools such as a text editor or IDE (Integrated Development Environment), bug tracker, version control system, web browser, and search engine (for finding all of the information you've forgotten). Those are just the basic programmer's tools, though. There are as many programming tools as there are programmers, and you'll pick up a bunch more and maybe even build your own over time.
As you get deeper into the bowels of programming, you'll find that you need to learn more about the surrounding environment. Security quickly becomes an issue that you need to learn well. Practical operating system knowledge, not just the theory of memory management and file systems, but the actual functional knowledge of the operating system you're developing on and developing for, will become increasingly important. System administration and server maintenance will likely come into play as well.
The learning doesn't stop with programming languages and coding tools. There are all kinds of methods and processes that you can learn to assist in building software, and some are more helpful than others. There's UML (Unified Modeling Language) diagramming, unit testing, TDD (Test-Driven Development), agile methods, refactoring, code construction advice, user interface design, and various forms of requirements definition. Of those I would put unit testing and refactoring near the top of the list, but your circumstances may dictate a different ranking. And then there's project management, which is another field entirely with many philosophies and strongly held opinions.
Finally, there is the domain specific knowledge that you'll acquire for the specific type of programming that you find yourself doing. This type of knowledge is so unique and varied that it defies enumeration, but examples include web design, embedded programming, scientific programming, modeling, tool building, mobile application development, and game programming.
Stepping Back To Take It All In
Now that is certainly an incredible amount of knowledge to try to acquire. Most of these topics could take years of study in and of themselves to even begin to master. If you are feeling a bit overwhelmed, you are surely in good company. Rest assured that no one could maintain a deep and lasting knowledge of all of these things.
The good news is that you don't have to. Part of being an effective programmer is knowing what you need to learn now and what can be put off until later, sometimes indefinitely. And once you have a good foundation, a programming language or two under your belt, and productive habits in at least one programming environment, you can pick off other topics as they become necessary (or desirable) to learn. The important thing is to keep chipping away at new things. You can gain a cursory understanding of any of these topics in a matter of days or sometimes hours, and then decide whether it's worth it to plunge deeper into the topic.
That still leaves the question of how to crack into the knowledge that you decide to pursue, but I'll leave that until next week. Regardless of how the knowledge is attained, it's amazing that there is so much to be had, and it's all intertwined under the canopy of software engineering. I, for one, can't wait to learn something new. The only real problem is choosing what to do next.