In recent years, it's become rather clichéd to refer to full-stack developers. Job postings specify this requirement, recruiters ask about it, organizations want it, and developers call themselves it. While almost everybody has a definition, not all definitions are created equal. The question still exists. So what is a full-stack developer? Apparently, it depends on what the meaning of 'is' is. As usual, information without context is just information. However, information with context is the start of a decision-making framework. Too often, information is only analyzed from a single perspective. This has resulted in countless missed opportunities; recruiters, hiring managers, and candidates alike. As such, it's important to start this conversation by setting the context.
At one point, software developers called themselves software developers (or coders). And then it become trendy to refer to them as software engineers. However, many didn't realize that there were some things that distinguished software engineers from software developers. The primary difference was in the breadth of knowledge. Both developers and engineers may have deep knowledge in specific areas, but the engineer has more areas of knowledge. The software industry recognized that there was a distinction and a need for both specialists and generalists. Since the term software engineer had become 'watered-down', the term 'full-stack developer' was born.
While this is just one perspective, and I realize that not everyone will agree with it, below are some of the areas of knowledge that I feel are important for 'full-stack developers', regardless of your preferred technology stack.
Data structures aren't just for college. While building a web application with any number of frameworks that are available today may not require you to flex your data structure muscles, the need for this knowledge has not been abstracted away. Most systems that have complexity beyond just being a form-based web application will require in-depth data structure knowledge at some point. Stacks, queues, heaps, maps, and trees. Every one of these are commonly used in modern architectures. While you could use any one of these (take your pick) for just about any type of solution you dream up, there are cases where one is much more appropriate than another.
If you don't know what Big O notation is, check this out. It turns out that runtime efficiency is kind of important. While it may not have mattered for the toy applications you built in college, it's a big deal when working on systems that produce millions of transactions per second. It's not enough to just 'make it work'. Your algorithms need to be efficient as well.
It's pretty much impossible to have a conversation about searching and sorting algorithms without getting into data structures and and runtime complexity. You need to know and understand search/sorting algorithms for the same reasons you should know and understand data structures and runtime-complexity. This knowledge will come in extremely handy when you are working with large data sets. Here is a useful cheat sheet on the runtime complexities of various search/sort algorithms.
You will need to understand the following five object-oriented principles as explained by Robert C. Martin. (SOLID) Single-responsibility principle, open-closed principle, Liskov substitution principle, interface segregation principle, and dependency inversion principle. If you don't understand what these are, you can start your research here. Know what these are, and why they are important.
Given enough time, most developers could probably turn a set of requirements into working code. However, some implementations will be pretty much impossible to extend or modify. Others will be so complex that it's incredibly difficult to figure out what's going on (even for the implementor).
Design patterns are fundamental to good software design. Most problems encountered by software engineers have already been solved by countless others. Recognizing common problems that exist and the solution patterns that are commonly leveraged makes development significantly easier, and will make your code much cleaner. If you haven't read the authority on this topic, Design Patterns: Elements of Reusable Object-Oriented Software, you should order it right now! This book should be on every software engineers bookshelf (and not just collecting dust). Another decent book on design patterns that may be somewhat easier to understand is Head First Design Patterns.
There are times when a recursive function can be an elegant solution to problem. There are other times when a recursive function is the problem (ie. You just filled up the thread stack unintentionally). Either way, you should have enough knowledge about recursion to understand what use-cases are appropriate.
Flexibility or performance. In a nutshell, the difference between static and dynamic languages. Dynamic languages are extremely flexible, and you can do things at runtime that are not possible with static languages. However, ability to interpret at runtime comes at the cost of performance. For the basic differences, see this post. For a listing of some dynamic languagues, look here.
You should possess a solid understanding of relational database concepts and SQL. Check out this link to see a really interesting visualization of SQL joins. You should be familiar with query performance optimization techniques, and understand benefits and drawbacks of database indexes. You also should possess an understanding of the various types of NoSQL database technologies:
Even if you don't have experience with multi-threaded applications, you need to understand some basic threading concepts. This includes (but is not limited to) synchonization, scheduling, deadlock, starvation, race conditions, thread lifecycle, initialization, and usages. A number of languages have rather extensive threading libraries. You should learn these libraries and understand their usage.
With the ubiquity of cloud-based systems, almost every company is vulnerable to a data breach. Remember "defense-in-depth". You should conceptually understand the various protections that can be added to make data more secure through each layer of an application.
You should understand basic application security concepts and mitigation techniques. Some examples includes cross-site scripting, cross-site request forgery, injection vulnerabilities, and insecure direct object references. The (OWASP) Open Web Application Security Project is a great resource for application security information. Check out the OWASP Top 10 2013 - PDF.
If you've worked in enough application layers to call yourself a full-stack developer, then you should understand some basic of software and systems architecture. A big-picture understanding of how the various components of a complex system interact is important. Working in a full-stack environment requires and understanding of the interaction model between various layers and components. You should have a working knowledge of a number of common/popular architectural frameworks and patterns.
Web services are not just for spiders. Just about every modern organization has a public API. While REST APIs that leverage JSON over HTTP are quite common, XML-based SOAP web services are still in existence in some organizations. You should have an understanding of the latest technology trends, the advantages and disadvantages of different web services types, and how the microservice movement fits into all of it.
There are not a lot of self-contained applications these days. Most applications interact with numerous disparate systems, and it's important to understand conceptually how these systems interact. You should know how to build a system that scales-up as the system load increases. You should understand how cloud-based architectures can be leveraged to accomplish resiliency and scalability within an application.
While not every language supports reflection, you should be aware of use-cases where it might be useful to extract class meta-data. Many common frameworks make extensive use of reflection. You should be able to speak to advantages, disadvantages, and the appropriate contexts where reflection might be a good fit. Many dynamic languages use reflection for much of the behind-the-scenes 'magic' the makes these languages so flexible.
There are numerous technology-specific tools that can be used for performance profiling. At a minimum, you should know which tools can be used for performance diagnostics in your particular technology. You should also have a basic familiarity with the features of the most common performance profiling tools.
Manual testing is labor-intensive and costly. You should understand the various test automation frameworks and strategies used within your technology stack. This includes unit, integration, and client-side testing frameworks. Most technologies contain fairly advanced testing frameworks, and it is critical that you know how to leverage them.
Wow! Maybe you did realize that calling yourself a full-stack developer comes with such responsibility. Maybe you have all these topics under control, as well as numerous others that I've missed. Either way, I hope the next time someone mentions 'full-stack developer', you know exactly what they are talking about (even if they don't).
However, while skills are an important part of developer productivity, competencies are actually a better indicator of software engineering excellence.