How to Think Full-Stack
I’d like to share some thoughts on what it means to be a full-stack developer and how to think full-stack while building a web application.
But first, a disclaimer: I wouldn’t call myself a full-stack developer the way many understand the job. Being a true full-stack engineer is rare these days, as technology and language advancements add layers to the stacks we work in. I do, however, work on features that cut across layers of the stack. I also feel stronger working in some layers over others, and often seek out guidance from others. But the way I think about it, full-stack is usually more of a goal than a title. And even without reaching that goal just yet, thinking like a full-stack developer is achievable for engineers at all experience levels. This skill will help you build applications with clean architectures that are easy to maintain and grow.
Full-stack Development: What is it?
If full-stack development isn’t being a total master of the stack, what is it? According to the Stack Overflow Developer Survey results, full-stack web developer is the second most common developer occupation. Accordingly, there are a lot of definitions out there and every job posting will expect something a little different. But basically, it’s what it sounds like. There’s this “stack” of layers in a web application and a full-stack developer has familiarity in all (or most) of them.
What stack am I talking about?
At the top are the more practical layers that are closer to the customer. Having product and business expertise is how you understand the needs of your customers. This layer involves understanding the business value of what you’re building, which helps you make the correct strategic decisions that will delight your customers and meet their needs. This, in conjunction with the User Experience (UX) layer, focus on making it easy to use your product the right way and hard to use it the wrong way.
Further down are the more technical layers, where you'll start seeing code. First, the User Interface layer (the "frontend") is where you display application data to the user in readable layouts. This also includes frontend data models/data flows and the user interactions within the application. The API layer largely bridges the communication gap between frontend and backend. It is important that it connect the various services of your application effectively. The Data Modeling layer is all about creating a conceptual model of how the data in your app is structured, which can be persisted in relational or non-relational data stores. The Backend Logic layer contains the code needed to perform the business logic of the app. It tends to handle the heavy lifting in terms of processing and calculations. And finally, the Infrastructure layer encompasses the many concerns of development and operations, including network resources, storage, data redundancy, and availability, to name a few.
Full-Stack Skills and Concepts
But I personally think that’s a lot for any one developer to know, and it’s a lot more than I knew when I got started as a full-stack developer at Qualtrics. Instead, it’s enough to start by having some familiarity in one or two levels of the stack and an interest in learning about the others. The goal is to make decisions in one area of the stack that will be efficient and clean to work with in other layers. But really, it takes a long time and extensive practice to be a specialist in all of the layers. A big part of being a full-stack developer is being excited about trying new things, but also asking for help from the specialists on your team (experts in certain technologies or layers of the stack) when you need it.
The Value of Full-Stack Development
Since full-stack development requires a lot of knowledge and experience, why do we try to do it anyway? First, full-stack development, when done well, allows a segmented application to feel more unified. Say you’re working on a frontend task, for example. It can be enough to just consume or write to an API that someone else on your team has written or that you have designed together. But if you understand the datastore you’re using and how the backend reads from and writes to that datastore, you’re going to be able to design better data models and also better API’s that fit logically into your frontend components.
As a full-stack developer, you bring a complex and diverse perspective to the table. You understand your customers, the consumers of your work, and the context in which they use your application. This helps you build a better, more sellable product. You can also understand the roles your teammates play in specific parts of the stack and can make their jobs easier by considering the limitations and benefits of the tools they use. This creates a team dynamic based on empathy and common interest.
Full-Stack Development in Action
To show the value of thinking full-stack, I’ll walk through a feature I worked on a few months back and discuss some decisions I made about how to build it using a full-stack mindset. I’m hoping this will provide some concrete examples so that you can apply this thought framework to your own work.
I work on the team that builds Qualtrics Stats iQ, an intuitive statistical analysis application that empowers analysts of all experience levels to look at statistical relationships in their data. To do this, we parse user datasets (usually collected via Qualtrics surveys) into sets of variables that can then be related and analyzed in various ways.
Sometimes users want to create derived variables, i.e. variables that either combine data from multiple sources or involve further processing of data associated with a single variable. Consider, for example, a researcher who collected customer data about age in numbers, but wanted to bucket those ages into categories like “18-34”, “35-54”, and “55+” so they can examine the interests of those age groups. They could do this in Stats iQ by creating a derived variable based on their existing age data with our variable creation interface.
A Full-Stack Feature
This variable creation interface is what I’m going to talk about today. It’s evident that this will be a feature that will benefit from a full-stack mindset. Why? Well, we need to provide an interface for our users to define their variables. That means we’re thinking about our users and what they need out of our variable creation interface (product expertise layer, UX layer), as well as planning the data models and UI components that power this experience (user interface layer).
After the variables are created, our users are going to want to be able to run analyses on them. The effort we spent defining the variable would be wasted if we couldn’t do anything with what we made! This means we’re also considering backend layers, like how we model the definitions for these created variables (data model layer), update those definitions (API layer), and persist changes between sessions (data storage layer).
Now we know that we have a full-stack problem, we can tackle a few design questions in a full-stack way, starting with the data model.
At the data modeling/storage layer, I had to consider the trade-offs of using a longtext type (the column type for JSON blobs in our MySQL store) because they tend to be less searchable. But since our database operations always grab the entire value at a time, the lack of searchability turned out to be an irrelevant concern. And finally, in addition to being convenient at the UI, API, and data storage layers, the stringified objects can also be parsed as a Python dict in the backend for easy computation. Problem solved!
Speaking of computations, a big part of working full-stack is determining where computations should live. I knew the backend would be in charge of taking a variable definition and creating a column of data from it, but what about the computations involved with actually building the variable definition? Recall our researcher from before who wanted to bucket their age data into categories ("buckets"). This bucketing is a common use case for our variable creation users. In some of these cases, we think we can provide default buckets for users based on their data to get them started. Should those default buckets be computed on the frontend?
For me, the answer was to handle this (and pretty much everything) in the browser to improve responsiveness. The computations weren't complex enough to benefit from the additional processing power in our backend, so handling computation in the browser avoided a network delay in receiving computation results.
I’ve only given a few specific examples, but every stage of the development process on this feature involved consideration of other layers. And through all of those stages, I had help - my manager, my PM, our UX resource, my teammates. I’ve learned that using the resources I have available is critical to making the right decisions, especially when I don’t have the most fresh or extensive context. Thinking full-stack (and involving the experts) as I went helped me to avoid entire rewrites. I was able to anticipate at least some potential problems in advance. This is why it’s important to take the time to think full-stack in the architecture and design phases.
Being full-stack is a moving target, but one that I’m chasing after and you can, too. That said, if you heard this and thought “Yeah, that sounds like a lot of work” or “I’m just not that interested in (blank) layer”: that’s awesome, too. Every team needs specialists in certain areas because you can become the kind of rock star advisor that full-stack engineers rely on to make the best decisions. But having developers in the mix who do think full-stack help unite the team and the product by designing frontend and backend components that work smoothly at all layers in the stack that can be maintained and extended for years to come.