A platform stops generating 400 lines of observability and deployment boilerplate into each new service repository and ships it instead as a versioned library plus a shared base chart that services depend on. Two hundred services will adopt it. What is gained and what is paid and when does the bill arrive?
Show the full answer Hide the answer
What is gained
Reach. With generated code, an improvement lands in new services only, and the 200 existing ones diverge; reaching them means 200 pull requests and a year of nagging. With an inherited library, a fix ships once and arrives everywhere that bumps a version — days rather than quarters. Two numbers make this concrete: the share of the fleet running the current major version, and the median age of the dependency across the fleet. Aim for a median under 30 days and you have a platform that can actually change something.
Consistency comes with it. Generated code is edited within weeks, so a fleet built from templates is an archaeology of every template version ever shipped. Behaviour behind one interface is the same everywhere, which is what makes estate-wide statements about the fleet true.
What is paid
The same reach, in the other direction. The platform is now in the runtime dependency graph of 200 services, so a bad release can degrade all of them in an afternoon. Generated code has a slow, cheap blast radius; a shared library has a fast, expensive one.
Three further costs land on the platform team:
- Version support. Teams bump at different times, so you support the current version and the two behind it in practice, including their configuration formats. A deprecation you announce is a migration you will be funding.
- Debugging across the boundary. Stack traces now pass through platform code inside someone else's process. Unless the contract names the pager, every defect starts as a dispute about whose problem it is.
- Language lock-in. A library commits you to a runtime. The second language in the estate either goes unserved or doubles the work, which is the point at which an out-of-process approach starts to look cheaper than a library.
When the bill arrives
At the first bad release, and at the first breaking change you genuinely need. Both are survivable if three things exist beforehand: a canary cohort of five low-tier services that take every version first for a week; a compatibility suite run against the top 20 consumers in the library's own pipeline; and automated version-bump pull requests with auto-merge only when the consumer's own tests pass. Never combine automatic bumps with unpinned versions — that is a fleet-wide deploy nobody scheduled.
Keeping the option to reverse
Keep the boundary thin enough that a team can drop the library and wire the same four concerns by hand in a day, and keep one reference service that does exactly that, tested. The escape route stops a single unmet requirement from becoming a defection, and it keeps the platform honest about how much the library is really doing.
Common weak answers
- "Libraries are better than templates." They are better at reach and worse at blast radius. The choice is which failure you would rather manage.
- "Use both." Common and workable, and only if you say which concerns are inherited and which are generated. Otherwise you own both failure modes.
- "Generated code is duplication." Duplication that a team owns and can read at three in the morning is sometimes the cheaper defect. When the estate is under about 20 services, or one team owns all of them, generated code plus a README beats a library and a support commitment.