A new feature does not necessarily require a new architecture.

That was one of the principles behind adding a Read Aloud capability to the Fern ICT website. The requirement was straightforward: allow visitors to have an Insight read to them, with simple controls to start, pause, resume and restart the reading. On the surface, this sounds like a small user-interface enhancement. It would also have been quite easy to turn it into something considerably more complicated.

I wrote previously about how the Fern ICT website was deliberately built as a small static system, starting with what the website actually needed rather than with a particular technology or framework. That approach created a useful constraint when the Read Aloud requirement emerged. The question wasn’t really “How do we build text-to-speech?” It was “How can we add text-to-speech without undermining the architecture we already have?”

Start with the architecture you already have

The website is deliberately simple. Content is written as Markdown, processed at build time into HTML, and then served as static content. There is no application server handling requests for individual pages, no database behind the content and no dependency on a collection of runtime services. That simplicity is valuable because it reduces the number of things that can fail and makes the site relatively easy to understand, maintain and deploy.

Modern browsers already provide speech synthesis through the Web Speech API. That meant the Read Aloud capability could run entirely in the visitor’s browser. There was no need to introduce a speech-generation server, subscribe to an external text-to-speech API, generate and store audio files, or manage another set of credentials and operational dependencies.

The architecture therefore remains essentially the same: Markdown becomes HTML during the build, and the browser presents that HTML to the user. Read Aloud is an additional behaviour applied to the content at the edge, rather than a new service inserted into the architecture.

That is a small distinction, but it is an important one. A feature can be valuable without becoming a new architectural layer.

Keep the feature close to the content

The implementation follows the same principle. A small JavaScript component identifies the meaningful content of an Insight and divides it into manageable sections for speech synthesis. Headings are treated separately from the surrounding text so that the structure of the article remains apparent when it is being read aloud. The controls provide the expected interactions, while the section currently being read is highlighted on the page. The controls also remain available as the reader scrolls through a longer article.

None of this changes the underlying content model. The Markdown remains the source of truth and the generated HTML remains the published artefact. The Read Aloud functionality simply adds another way of interacting with that content.

This is an example of a useful architectural boundary. The content system does not need to know how speech works. The browser does not need to know how the site was built. Each part can continue doing the job it was originally designed to do.

Even the reading time is build-time

The reading-time indicator was approached in much the same way. Rather than calculating the reading time in the browser every time someone opens an article, the build process calculates it from the article content and generates a simple value such as “3 min read”.

That keeps the published page deterministic and avoids adding client-side processing for something that can be established when the site is built. It also means the reading-time value is derived from the same source content as the article itself, rather than becoming another piece of information that has to be maintained separately.

The distinction between source and generated output was one of the deliberate architectural choices described in the original website article, and it continues to be useful as the site evolves.

Add regression protection, not infrastructure

There is another lesson here that is easy to overlook. Once a feature becomes part of a website, it becomes part of the assumptions that the build process needs to protect.

The build validation was therefore extended so that a successful build now verifies that the Read Aloud component exists, that the expected controls are present on every Insight, and that the generated reading-time value is present and valid.

These checks don’t attempt to determine whether a particular browser’s speech engine sounds good. That remains a browser and device concern. Instead, the build checks protect the architectural contract: if the site is built successfully, the generated site should contain the components we expect it to contain.

This distinction is useful more generally. Not every problem needs to be solved at the same architectural level. Some requirements belong in the build process, some belong in the browser, and some genuinely require new infrastructure. The challenge is recognising which is which before reaching for another service or another layer of complexity.

A small feature, deliberately contained

The finished Read Aloud feature is a fairly small addition to the website, but it is also a useful example of the engineering approach behind Fern ICT.

The requirement changed and the user experience improved, but the underlying architecture did not need to become more complicated to accommodate it. No new application server was introduced. There is no database, speech service or external API sitting behind the feature. The content model did not change, and the deployment model did not change.

Good architecture isn’t about preventing change. It is about making change possible without unnecessarily increasing complexity.

Sometimes the best architectural decision is simply to use what is already there.

That is particularly relevant when working with AI-assisted development. As I wrote in the original article about building the Fern ICT website, AI can make exploration and implementation considerably faster. But faster implementation also makes it easier to add things simply because they can be built. The important question remains whether they should be built, and where they should belong if they are.

The Read Aloud feature is a small example of that principle in practice: useful new capability, deliberately contained, with the existing architecture left simpler than it could have been.