SEO for Solos
Chapter 10Free

Content and E-E-A-T

Author identity that resolves, freshness dates that are actually maintained, claims traceable to evidence, and why publishing your own failures is the strongest trust signal available to a new site.

5 min read · updated 2026-07-26· last reviewed 2026-07-26

Author identity has to resolve to something

The principle

An author is either an entity or a string. A string on a byline connects to nothing. An entity has a page, a stable identifier, a description of why this person is qualified to write this, and a link from every article they wrote.

The mistake in both directions is common. Sites either omit authorship entirely, or they invent it: a stock headshot, a fabricated job title, three social profiles that do not exist. The second is worse, because every fabricated detail is trivially checkable and the whole page becomes suspect when one of them fails.

Omit fields you cannot support. An author entity with a name, a role and two honest sentences is stronger than one with twelve fields, four of which are invented.

The implementation

typescript
// lib/authors.ts
export type Author = {
  slug: string;
  name: string;
  role: string;
  shortBio: string;
  longBio: string;
  url: string;
  /** Only profiles that exist. An empty array is honest. */
  sameAs: string[];
  /** Legacy byline strings that map to this author. */
  aliases: string[];
};

export function resolveAuthor(byline: string): Author | undefined {
  return AUTHORS.find(
    (author) => author.name === byline || author.aliases.includes(byline),
  );
}

Alias resolution is what stops three spellings of the same name becoming three authors, which is the most common way an author entity gets diluted on a site with any history.

typescript
export function profilePage(opts: ProfilePageOptions): JsonLdObject {
  return compact({
    "@type": "ProfilePage",
    "@id": `${opts.url}#profilepage`,
    url: opts.url,
    name: opts.name,
    mainEntity: opts.mainEntity,
  });
}

Freshness dates are a promise

The principle

A "last reviewed" date is a claim that somebody looked at this content on that date and confirmed it was still true. Bumping it automatically on every build converts a trust signal into a false statement, and it is the kind of false statement that is easy to catch: a chapter about a deprecated API with last week's review date tells the reader everything about how the site is maintained.

Two dates, and they are different:

  • updatedAt is when the text changed.
  • reviewedAt is when someone confirmed it was still correct without necessarily changing it.

dateModified in schema should track updatedAt. The visible "last reviewed" line should show reviewedAt.

The implementation

typescript
const frontmatterSchema = z.object({
  updatedAt: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  reviewedAt: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
});

Both required, both validated at load time, so a chapter cannot ship without them.

Every non-obvious claim traces to evidence

The principle

This is the discipline that separates a reference from an opinion piece, and it is also the only practical way to keep a long technical document accurate as the platforms under it change.

Three acceptable sources, and nothing else:

  1. Code. The mechanism is implemented and covered by a test.
  2. Primary documentation. Google Search Central, Bing Webmaster docs, an operator's own bot documentation.
  3. Labelled opinion. A judgement call, marked as one.

Maintain the mapping in a file. When a platform changes, you grep the file for the affected claim rather than rereading twenty chapters.

The implementation

markdown
<!-- content/CLAIMS.md -->
| Claim | Chapter | Evidence |
|---|---|---|
| Blocking GPTBot does not remove a site from ChatGPT's search index | bot-economics | https://platform.openai.com/docs/bots (separate OAI-SearchBot agent) |
| A canonical chain is followed for one hop | metadata | Google Search Central, canonicalization documentation |
| Deny training crawlers by default | bot-economics | Opinion. Cost with no measurable return. |

Publish what you get wrong

The principle

A new site has no testimonials, no case studies and no brand. The strongest trust signal available to it is specificity about its own limits.

A proof page showing 100 percent on everything is less credible than one showing 94 percent and naming the six failures, because the first is what a marketing page says and the second is what an audit says.

This is not a rhetorical trick. It works because it is checkable, and because a site willing to publish its own failures is unlikely to be hiding others.

The implementation

Generate it, so it stays honest by construction:

typescript
const results = await runAutomatedChecks();
const failures = results.filter((result) => result.status === "fail");
await writeFile("docs/SELF_AUDIT.md", renderReport(results));

If a score drops, the page shows the drop. That is the entire mechanism, and it is why the page has to be generated rather than written.

No invented social proof

The principle

An aggregateRating with no reviews behind it is a manual action risk and a lie. Invented testimonials are worse: they are checkable, they are shared, and the first person who notices will say so publicly.

If you have no social proof yet, use the numbers from the work instead. "Audited 2,100 URLs and 42 schema types" is proof of something real. "Loved by 10,000 developers" is proof of nothing when it is your launch week.

Checks this chapter covers

Each one has a command you can run against your own site.

Product analytics are optional. On the skill page, X advertising measurement is also optional and shares your visit and ad identifiers with X. Both are off until you allow them. Privacy policy.