Build only what differentiates you. Manage technical debt like financial debt, deliberately and visibly. Govern with guardrails and paved roads, not just approval gates.
π οΈ Build
Full control and a perfect fit. You own every bug, upgrade and on-call page, forever. For what makes you different.
π¦ Buy (COTS, self-hosted)
Proven features, vendor support. You still host and patch it. Customising it heavily makes upgrades painful.
βοΈ SaaS
Fastest to start, no infrastructure. Less control over data location, roadmap and outages. Per-user costs grow with you.
π§© Open source
No licence fee and you can inspect the code. You need the skills to run it, and you should check the licence and project health.
π Debt register
A visible list: the item, its impact ("adds 2 days to every release"), the interest (growing cost), the owner and a repayment plan. Link debt to ADRs so the reason is kept.
π° Debt budget
Reserve a fixed share of every sprint or quarter (often 15β20%) for paying down debt, instead of waiting for a "refactoring project" that never gets funded.
π Fitness functions
Automated checks that guard an architecture characteristic: dependency rules, p95 latency, no vulnerable packages. They run in CI, so debt can't creep back in silently.
Design authoritysets principles & standards
β
Paved roadtemplates, platform, defaults
β
Guardrailspolicy as code, fitness functions
β
Teams buildautonomously
β
ADRsfor exceptions & big decisions
.NET 10 How to implement it
| Fitness function | Library / API | Notes |
| Dependency and layering rules | TngTech.ArchUnitNET (+ TngTech.ArchUnitNET.xUnit) | Architecture rules as unit tests that fail the build |
| Vulnerable or deprecated packages | built-in dotnet list package --vulnerable --deprecated | Run in CI to stop dependency debt from piling up |
using static ArchUnitNET.Fluent.ArchRuleDefinition;
public class ArchitectureTests
{
static readonly Architecture Arch = new ArchLoader()
.LoadAssemblies(typeof(Payment).Assembly, typeof(PaymentsDbContext).Assembly)
.Build();
[Fact]
public void Domain_does_not_depend_on_infrastructure() =>
Types().That().ResideInNamespace("Payments.Domain")
.Should().NotDependOnAny(Types().That().ResideInNamespace("Payments.Infrastructure"))
.Check(Arch);
}
Build vs buy is really "what makes us different?" π§
π οΈ Build what differentiates you: pricing, customer experience.
βοΈ Buy or subscribe for commodity: payroll, ticketing, identity.
π° Compare 3β5 year TCO, not the licence price.
πͺ Write the exit plan before you sign.
π Then manage tech debt like real debt: a register, a budget, and fitness functions in CI.
π€οΈ Govern with guardrails and paved roads, not approval queues.
What's the best build-vs-buy call you've seen?
#SoftwareArchitecture #TechDebt #BuildVsBuy #EnterpriseArchitecture