The scope of your work drastically changes
Working exclusively in the frontend or backend means you rarely have to consider the other side of the ecosystem that you work in. When moving to full-stack development, you have to consider all moving parts within the whole ecosystem and pick your approach based on the outcome that you’re aiming for.
Grasping this takes time and practice as you have to consider all the costs of an approach to solving the problem. For instance, when deciding on the choice of database a backend engineer may just decide based on the type of data that they are storing and reading, some databases are better for more read-heavy usage. However, a full-stack engineer has to also consider how the front-end may want to read the data and if some benefits of caching will be beneficial for the user experience. Not only do you need to consider the performance and extensibility of the backend but also how it will be rendered in the frontend and what tradeoffs can be made in between.
Wider Team Interaction
There are different stakeholders when implementing frontend and backend solutions. Writing a solid API in the backend only needs to know what the requirements of the requests and responses from the frontend will be and then everything else is your own implementation detail. Creating a good application screen in the frontend means taking designs and product decisions and making sure that you can actually deliver on what they want the screen to look like.
Working on the frontend means expanding on softer skills to be able to negotiate and make user-driven design choices where there will be some difference in opinion about what a user may want to see. This was quite a change for me as coming from the backend world I would quite happily just gather the requirements of what the input and expected output of the system was and then build that logic in whatever framework I wanted, it is rare that you can’t deliver on a given schema in any language or framework in the backend.
The difference here is that designs can look amazing, but writing them consistently in native Android and iOS code can be more of a challenge. There are remedies for this such as Flutter or React Native where you can build a more consistent design once for both platforms, but writing native apps means knowing the limitations and challenges of each platform and what can be done to work around them.
Total confusion at how mobile lifecycles work
Coming from a backend world, you have a good understanding of how servers fulfil the requests of a client but little knowledge about how the rest of the lifecycle of an API call. There are so many extra cogs to the machine that you don’t work with day to day, things like client caching and even some gotchas that just completely catch you out like leaking a ViewModel in Android.
The lifecycle events of Android and iOS have some differences and while trying to write the same code on both looks fairly similar overall there are some subtle differences in the way that views are transitioned between and how different states are handled, there is an excellent StackOverflow answer to how they compare and the most notable bit being that Android handles restarts to the activity separately.
Completely different paradigms of programming
One drastic change is the paradigms of working, coming from the backend it can be quite daunting when you see mobile-specific patterns such as MVVM pattern in Android and iOS. The biggest change for me was working with Reactive (or Rx for short) streams of data using RxKotlin for Android and RxSwift for iOS. Using Reactive streams have become quite commonplace in some frontend frameworks, with Apple’s SwiftUI having very similar concepts with different names built-in, and are making their way across the full stack via the ReactiveX APIs across all platforms.
Adjusting to working with observables, operators, singles, subjects and schedules in the ReactiveX API takes time and practice, but is just a change in thought process when deciding how to approach a problem. If you’ve never seen Reactive programming before, here’s an excellent short introduction by André Staltz.
Personally speaking, transitioning to full-stack is hard work as there is a lot to learn, I feel lucky that the company I moved to had very helpful colleagues who always had time to walk through new concepts to me and had the patience to show me where I had made common mistakes that seem like they should work but don’t behave as expected.
The traditional view of the frontend being the way something “looks” and the backend being the way something “works” has changed over time and I believe that the landscape of a full-stack engineer has adjusted to more of an overview of the architecture choices and decision making on what best suits the problem or project you are undertaking.