Not too long ago I was explaining to a group of developers a few reasons why database development and deployment is more challenging than application work. As I was talking about the different objects and the dependencies between them, an analogy occurred to me.
The database is like a series of micro-services.
We have these objects that are compiled and built separately, but with dependencies between them. A view depends on tables. Stored procedures and functions might depend on tables or the reverse might be true. Each object essentially has an API, which is the structure of the table or other object. Each could be developed and deployed independently, and there could be conflicts that might be noticed at run time due to deferred name resolution.
Just like micro-services. In the case of application micro-services, each has an API and exists independently of others. They can be deployed, upgraded, and altered separately. If there isn’t coordination with other micro-services, there could be problems with how the entire application works.
Relational database development in teams is always challenging, precisely because each item can be independently build and compiled. Unlike a C# or Java compiler, all of your code isn’t evaluated at once. While you can use a technique such as schema binding to enforce some checks across objects, these are crude mechanisms that interfere with a lot of development changes, especially those where we are trying to approach zero downtime deployments.
There is also power in this independence, allowing us to make small changes over time. If we structure a series of deployments well, we can achieve very close to zero downtime changes, though usually at the expense of extra space and processing for a period of time.
As I think about each table, view, procedure, and function being a separate service, I can more easily understand the dependencies that exist between them. I realize there is a need for close communication with other teams. Developers know that they need to coordinate changes across teams when their service API changes, and they can apply the same idea to the database. They can also make some changes without worry, as we can ensure backwards compatibility in many cases.
A lot of the challenges in database development feel less daunting when we re-frame them. Once we stop viewing the database as a single thing and realize that each object is independent and has a contract, implicit or explicit, with other objects, we can more easily plan on how to make changes in a way that minimizes the impact on other teams.