Full-stack engineering sounds like a dream. In fact, it’s a recipe for slower development, lower-quality software, soaring technical debt, and overstressed engineers.
By Jeremy Duvall, founder, 7Factor Software
This article originally appeared on InfoWorld on December 12, 2022.
It’s a lovely fantasy. One developer to do it all—the magical fullstackicorn.
Hiring for your startup or tech-forward company? One full-stack engineer will give you 2x or more of what any regular developer could.
Starting or advancing your career as a software engineer? Full-stack engineering puts you on the fast track to senior positions twice as fast.
Full-stack engineering is an attractive legend, but in many cases it’s a misguided compromise that can produce a lower-quality product in exchange for making one individual more stressed.
If you’re a developer just getting started in your career, be wary of any job posting looking for a full-stack engineer. You’ll be expected to do two jobs for one salary, each in half the time.
If you’re hiring developers, don’t ask exclusively for full-stack engineers. The supposed upfront savings will cost you in malformed databases, a bucket of technical debt, and/or unnavigable user journeys.
There are some exceptions: specific tools in specific use cases where full-stack engineers can deliver perfectly functional code. (I’ll say more about these below.) And full-stack expertise can reasonably be an end game for very senior engineers with many years of experience. But in most cases, full-stack can be a choice to settle for less optimal solutions while setting up engineers to fail.
Does full-stack engineering deliver one-third the value? One-fourth? Whatever the math, the conclusion is clear: You’ll get much more value from a high-performing and experienced team of specialized engineers.
Our minds do not multithread
For all our many talents, human brains do not scale exponentially, and we’re terrible at parallel processing under heavy cognitive loads.
People who believe they multitask well turn out to be the very worst at it, and full-stack engineering is just a gold lamé wrapper around chronic context switching. Design a Boyce-Codd normal form database schema with proper indexes and implement a highly scalable RESTful call while building an intuitive user interface that surfaces interactions with the corresponding object model? Then support and maintain your full implementation along with the problem you were solving? Seems a little overwhelming.
Front-end and back-end engineering are equally complex disciplines, each with their own priorities and practices. Either one takes many years to master, and neither stands still. The learning never stops.
Full-stack engineering asks people to learn too much at once, a cognitive load that unnecessarily strains our brain’s capacity. These overloads slow down development and result in more mistakes that can lead to excess technical debt down the road. This is not a problem unique to less experienced developers. As long as the field continues to advance so quickly, even veteran engineers will struggle to keep up.
Imagine trying simultaneously to learn a new (human) language from an unrelated language group and a new non-Euclidean geometry, while applying both to solve an interesting engineering problem… all while the grammar rules and the fundamental axioms keep changing as you go.
It may seem heroic to rise to such a challenge, but engineering organizations are actually better served by a team that can divide and conquer the challenges while working together on the best solution. Acknowledging our limitations leads to a better outcome.
The land of the fullstackicorn
There is a limited land where the fullstackicorn can roam free.
As I said above, efficient full-stack engineering is a perfectly achievable goal in the career of a senior developer, though even such a distinguished engineer will generally create more value as part of a high-performing team that includes specialists.
For a slice of software engineering problems, any full-stack engineer working on their own can craft a viable solution. There are two use cases for this:
- Server-side rendered monoliths.
- Hacked-together MVPs (minimum viable products) or prototypes (sometimes a special case of #1).
Some problems are familiar and simple enough to solve with server-side rendered monoliths. Ruby on Rails, Django, Laravel… if the solution needed fits comfortably within the opinionated patterns these frameworks provide, then an experienced team of full-stack developers proficient in those frameworks will be able to build it efficiently.
But remember that, while a monolith may serve your short-term needs, there’s an upper limit to their complexity and viability under scale. You’re committing to certain bounded choices designed for a set of well-known problems. A monolith is not the best choice for all engineering challenges. You may well end up rebuilding your codebase later in order to shift to right-sized services or to implement a more innovative approach to a novel problem.
Similarly, early-stage startups are sometimes content to build a “loose” MVP as they’re seeking funding. (This may well be built on one of the server-side rendered frameworks.) Full-stack engineers can do this, but they shouldn’t be exploited or put under unfair pressure just because the startup can’t afford to hire a deeper team.
The key here is to go in open-eyed that you may end up rewriting the code from scratch when you get substantial funding. It won’t be a matter of refactoring or extending a malnourished MVP. You’ll likely throw it out and start over, building it more robustly with a qualified team of specialized engineers.
If everyone is OK with the limitations of full-stack engineering and willing to accept the consequences without later punishing the team for the codebase’s shortcomings, then let that fullstackicorn roam free.
But if you don’t want to run the risk of these limitations, there’s another way that could lead to a more optimized solution.
Full-Venn engineering teams
For many problems and opportunities in software engineering, collaborative teams of front-end and back-end engineers can be far more effective than full-stack soloists.
The back-end developer can focus on the data layer, well-designed RESTful endpoints, threading, scalability problems, avoiding the n+1 problem for queries, and so forth.
The front-end developer can focus on pleasant and intuitive interactions between user and application, efficient UI bundle downloads, and well-designed and reusable components.
Much of their work, each can do alone, fully focused on their specialty and benignly ignorant of the other’s concerns. But where their areas of responsibility come together—where the circles in the Venn diagram overlap—the team members collaborate to decide on the best solution.
What data will users need to access or edit? How will the UI call the API? What does the data contract look like? The team coordinates around these questions together, then each goes off to handle their part of the solution.
By bringing the team together to have these conversations, you do slow the process down to half speed for a short while, but then you go back to full speed once they part again, with the context required to go faster and implement features correctly in their own silos. (Meanwhile, the full-stack engineer spends the entire project at half speed, at best, with multitasking and context switching bringing everything to a crawl.)
As you might expect, given these overlaps of responsibility, it’s important for each team member to have some understanding of the other’s area of expertise. But it’s OK if this expertise doesn’t go very deep, particularly for engineers who are still early in their careers.
Careers are crafted in collaboration
There’s a tendency, early in the career of a software engineer—probably in other careers as well—to try to learn everything all at once. But this impatience actually slows down your progress.
Imagine trying to simultaneously pursue PhDs in math and biology in order to tackle some important problems in protein folding. With your attention and time divided, it will be many years before you complete even one of those doctorates. It will be many more years before you can make any meaningful contributions to the field, assuming the problem hasn’t evolved or been solved before you get there.
What if you decided instead to pick just one. Let’s say mathematics, with a specialization in statistical mechanics. Along the way, you reach out to your peers in molecular and cell biology. You form alliances. You assemble cross-disciplinary teams.
You progress much faster in your understanding of the math while your colleagues do the same in biology. You learn from one another, not enough to earn PhDs in each other’s fields, but enough to collaborate on some interesting problems. You also learn how to work together well: the process of it, the humanity, the social structures of a good team.
Even after you graduate, you each continue to stay on top of the latest developments in your respective fields. And you begin to make truly meaningful contributions to the problems of protein folding, far sooner and more sustainably than you ever could have alone.
And yes, you come to understand molecular and cell biology better than most mathematicians, though less well than most biologists. This gives you an edge in your career and in your ability to make a meaningful impact in your field.
The same is true in software engineering. You’ll progress faster if you decide to focus on, for a good amount of time, either front-end or back-end engineering, then learn to collaborate well on a team that includes people who chose a different specialty. Over the course of years, you’ll learn a lot through collaboration, and that cross-disciplinary knowledge will help you do more.
More fulfilling than full-stack
The end state of this career path could be a full-stack engineer, though by that point you’re positioned for so much more. You’re a software architect, maybe an engineering manager. You’re someone with a strong grasp of the fundamentals, some specialized knowledge in one area of the field, a familiarity with areas outside your expertise, and a big-picture understanding of how it all comes together to solve the most interesting problems.
That’s the real two-for-one promise, sometimes five-for-one or 10-for-one. Not one overburdened full-stack engineer who can do the work of two in twice the time and with half the quality. Rather, someone who has come up the pathway of a specialist and through cross-disciplinary collaboration developed a sophisticated understanding of how good software is architected.
Full-stack engineering is at best poor math that can lead to suboptimal implementations. The real value—for clients, companies, and individual engineers—comes from collaboration in high-performing teams.