← All writingAn essay by Sajag Silwal
Technology

Good systems start with good boundaries

Before queues, caches, and distributed everything, there is a simpler question: where does one responsibility end?

1 min read

Stacked modular blocks connected by thin lines on an olive background

A system is easier to change when its boundaries make sense. That sounds obvious until a small product decision requires edits across a database query, a route, a view component, and three unrelated tests.

Give each layer a clear responsibility

A repository retrieves information. A service applies the rules that make that information useful. A page presents the result. The value of this separation is not the number of folders it creates. It is the number of assumptions that can change independently.

interface ArticleRepository {
  findPublished(slug: string): Promise<Article | null>;
}

async function readArticle(slug: string) {
  const article = await repository.findPublished(slug);
  if (!article) return null;
  return presentArticle(article);
}

Design for the next real change

A boundary earns its place when it protects a likely change. Swapping a development fixture for an API is a useful example: the page should keep receiving the same kind of article, even though the retrieval mechanism has changed.

A good abstraction removes a reason for two things to change together.

Keep the contract small

  • Return the data the caller actually needs.

  • Validate unfamiliar data at the edge.

  • Keep failure behaviour explicit.

  • Test the boundary with a different implementation.

A small contract is easier to reason about and easier to replace. Start there. Add infrastructure when a measured need gives it a job to do.