Skip to content

Case study

Bates Solutions online presence

A company's whole online presence from one set of facts and one brand: two websites, a contact form, a resume, and LinkedIn, Google and GitHub profiles, built in a month.

Client
Bates Solutions
Dates
2026 to now
Live
bates-solutions.com (opens in a new tab)
days from the first merge to bates-solutions.com going live
6
hours of the owner's own time over the month
10-25
minimum mobile Lighthouse score, every category, every page, enforced on every change
95

The problem

A small business needs to be findable and believable: a site that loads fast and says what it does, a contact form that reaches someone, a Google listing, a LinkedIn page, and the same facts in every one of them. Most end up with those built at different times by different people, and the first thing to go wrong is that they stop agreeing. The site says one thing, LinkedIn another, and a two-year-old number sits on a page nobody remembers making.

In September 2026 Bates Solutions needed all of it at once: a company site, a brand to put on it, the profiles around it, and for me as the owner a personal site and a resume that held up. I built it as I’d build it for a client, and kept the process, so it’s now a service.

My role

I designed and built all of it. As the owner, I also did the parts only an owner can: approving the brand and the copy, confirming facts only I could vouch for, asking clients for permission to name them, and setting up the accounts on LinkedIn, Google and Meta. That took 10 to 25 hours of my time over the month.

What I built

  • A brand made as code. Colour and type tokens, a logo mark generated from geometry in four colourways, favicons, an email signature, and every social image (LinkedIn, Google, GitHub) rendered from templates. Change a colour once and every asset regenerates. A test checks the colours meet WCAG contrast.
  • One library of facts. Every project, role and number lives once, in one place. Every figure needs a source (a repository, a URL, or “first-hand” with a date), and a client is only named once they’ve said yes.
  • The company site. bates-solutions.com on Astro: services, about, contact, case studies with diagrams, light and dark themes. Every change has to pass a mobile Lighthouse score of 95 in every category, axe at WCAG 2.1 AA in both themes, full-page visual snapshots and a JavaScript budget before it can merge.
  • A contact form that protects the mail. One Lambda serves both sites’ forms: a Turnstile check, a throttle, then SES. Every form gets Turnstile because the email reputation is shared across the account, and one spam run can stop all of it.
  • The profiles. LinkedIn company page and owner profile, a launch post and seven weekly case study posts, Google Business Profile with a service area instead of a home address, Search Console and Bing for both domains, the handles reserved on five networks, and the business verified with Meta.
  • The owner’s side. mbates.net, a personal site in the shape of a terminal; a resume built to two pages as an ATS-safe .docx and a designed PDF, with a variant per application; and GitHub profiles for the company and for me, with their own banner and section images.

Architecture

Everything lives in one repository. Two libraries, the facts and the brand, feed four outputs: both websites, the resume and the profile images. The sites are static Astro builds on S3 and CloudFront, deployed by GitHub Actions through OIDC, so CI holds no long-lived AWS keys. Terraform owns the DNS, the certificates, the CDN and the contact Lambda, and every pull request on the company site gets its own preview.

Bates Solutions online presence architectureOne Nx repository holds two libraries: libs/content, the facts, projects and sourced figures, and libs/brand, the tokens, logo and images. Both build four outputs: the company site at bates-solutions.com and the personal site at mbates.net, each an Astro build on S3 and CloudFront; the resume as an ATS .docx and a designed PDF; and the profile images for LinkedIn, Google Business Profile and GitHub. Both sites post their contact forms through /api/contact to one Lambda that checks Turnstile, throttles and sends with SES. GitHub Actions runs the quality bars and deploys through OIDC, and Terraform manages the DNS, certificates, CDN and Lambda.one repository · Nx monorepolibs/contentfacts · projects · every figure sourcedlibs/brandtokens · logo · images, generatedbates-solutions.comAstro · CloudFrontmbates.netAstro · CloudFrontResumeATS .docx · PDFProfile imagesevery profile, generatedbuildContact LambdaTurnstile · throttle · SES · one for both sitesGitHub Actions and TerraformLighthouse 95+ · WCAG AA · OIDC deploys/api/contact
Two libraries, four outputs. Both sites post their contact forms to one Lambda; GitHub Actions holds the quality bars and deploys.

The hard part

Keeping every number true everywhere

The same figures appear on the company site, the personal site, the resume and LinkedIn: merged pull requests per project, years, hospitals. Counts like pull requests grow every week, so a figure that was right in September is wrong by November, and nobody notices because each copy looks fine on its own.

So the numbers aren’t typed into pages. Each count is defined once, re-counted from GitHub by a script, and locked in a file with its query and date. Pages and the resume read the lock, and a test fails if any copy of a number disagrees with it, or if a resume line states a count that nothing locks.

On 23 October the refresh showed every count had moved, Ocean Wise from 440 to 460 merged pull requests and Well-Plated from 357 to 394, and that the refresh itself had broken, because one repository had moved to a new GitHub organisation. One fix to the query, one run, and the test listed every page, profile draft and resume line still holding an old number. The site’s 26 visual snapshots that changed were checked, and the only difference in them was the numbers.

Outcome

  • bates-solutions.com went live on 6 October 2026, six days after the first merge.
  • mbates.net, the LinkedIn page and profile, Google Business Profile, Search Console and Bing, and both GitHub profiles followed within three weeks.
  • Hosting both sites costs a few dollars a month. Every account is in the business’s own name.
  • The process is written down as a playbook, and it’s now a service for small businesses.

Tell me what you're building