- Assuming PRD and UX are ready. The Frontend team take a look and come up with the Frontend data model they want. The focus is on making sure it's easiest for them to maintain and develop the client side.
- The Backend team take a look and come up with the Storage data model that is flexible enough for short term future. The Backend should anticipate the growth of the product and try not to be put in a situation where they have to redesign the database/infrastructure.
- Then both teams meet to negotiate the "Contract", which is the API layer. In some case, the API is more similar to the Frontend data model. In some case, the API is more similar to the Storage data model. In other case, there is a translation layer between Storage <-> API <-> Frontend (not as desirable, but not the end of the world). There are standard on how API should be designed (https://aip.dev/).
- When the contract is established, both teams can run more independently to each other, release at different times, and only needs to sync when there is a need for the contract to change. The API is designed to be easily extended, but not changed or deleted. The Backend team doesn't care if the Frontend data model exists. The Frontend team doesn't care what kind of Storage the Backend uses. It's an abstraction.
The complexity of the project can't be entirely eliminated. A good TL makes explainable trade offs on which parts of the system bear the complexity. It's their job to think not only about the architecture, but also how the development can be sustained in the future. This is especially true when the product is developed by a medium-large team. The focus is no longer on having the leanest/optimal code base. The focus is on making sure many parts of the system can move more independently, in parallel, and that it's scale-able to the growing business needs. It's a classic case of Amdahl's Law applicable to real life.
Of course, this is not always applicable to small projects that is never intended to have more than a couple engineers. Even then, when I work on solo full stack projects, I still unconsciously do this contract first approach.