The most useful thing I learned this week is something I cannot publish.

That is the normal condition of working in enterprise infrastructure and security. The interesting problems are interesting because they involve a real estate, with real constraints, real names and real consequences. Strip those out and it can look like there is nothing left to say.

Almost everyone resolves this in one of two ways, and both are bad.

The two bad resolutions

Publish nothing. Safe, and it leaves you with no external evidence of a decade of work. When it matters — a conversation about a role, a speaking invitation, a technical argument you want taken seriously — you have your word and a CV.

Publish generically. Far more common, and worse, because it looks like output while proving nothing. An article explaining what Zero Trust is, with no specific problem and no decision that could have gone differently, demonstrates only that the author has read the same marketing pages as the reader. Nobody has ever been hired off one.

The resolution is not somewhere between those. It is a different cut entirely.

Separate the lesson from the artifact

The artifact is the thing that exists at work: the configuration, the diagram, the script, the incident timeline, the architecture decision record. It is your employer’s property, it is specific to their estate, and it leaks.

The lesson is what you now know that you did not know before. It is yours. It travels. And it is almost always the part with the value.

A pipeline configuration is not transferable. “A copied pipeline is a dependency that cannot be patched centrally” is transferable, and it is the reason anyone would read about the pipeline.

Once the cut is made that way round, the writing gets easier rather than harder, because the specifics were mostly noise. The reader does not need your VLAN numbering to understand why flat network adjacency made per-application access hard. They need the shape of the problem and the decision you took.

Redaction leaves the shape behind

This is the mistake I see most often, and it is the one worth internalising.

Redaction feels like sanitisation. Take the real document, black out the sensitive strings, publish what remains. The trouble is that the structure is frequently the sensitive part, and redaction preserves structure perfectly.

A redacted configuration still shows:

  • how many hosts, sites or segments there are;
  • your naming convention, and therefore how to guess names you did not publish;
  • which vendors and products are in play, from the shape of the syntax;
  • how the environment is tiered, from the suffixes;
  • what is adjacent to what.

A redacted network diagram is worse. The boxes and lines are the diagram. Removing the labels removes the least informative part.

So I do not redact. I rewrite from scratch with synthetic values, and I change the shape as well as the strings — a different number of nodes, a different naming scheme, whatever count makes the example clearest rather than whatever count is real. If the example needs three servers to make its point, it has three, regardless of how many exist.

The test is simple: if someone who knows the real environment could recognise it from my example, I have not finished.

The checklist

I keep this in the repository next to the content, and I run it before every publish. It is deliberately boring.

Remove Because
Internal hostnames and URLs Reconnaissance, and they date the material
Tenant, account, subscription and project IDs Directly identify the estate and sometimes enable access
API keys, tokens, cookies, secrets Obvious, and still the most common leak in published code
Internal IP addresses and ranges Topology, and useful to anyone targeting it
Employee and customer information Not mine to publish, under any framing
Confidential diagrams Structure leaks even with labels removed
Private screenshots Metadata, window titles, browser tabs, adjacent notifications
Internal project and policy names Identify the programme and often its strategy
Commercially sensitive metrics Cost, scale and performance figures can be competitive information

Screenshots deserve a specific warning. A terminal screenshot carries the hostname in the prompt, the path, the shell history in view, and whatever is in the other pane. A browser screenshot carries the tab titles, the bookmark bar and any notification that arrived while you captured it. I now retype terminal output as text rather than screenshot it, which also makes it searchable, selectable and accessible.

Four questions before publishing

The checklist catches strings. These catch judgement.

Would this help someone targeting my employer? Not “is it secret” — “is it useful to an attacker.” Those are different questions, and the second is stricter.

Could a colleague identify the specific system? If yes, so could anyone else with partial knowledge, including a former employee.

Does it reveal commercial or strategic intent? Which vendor is being displaced, what is being consolidated, where investment is going. That can be more sensitive than any technical detail, and it is easy to give away while describing a migration.

If I remove every specific, is the lesson still there?

The last one is the important one, and it is the only one where the honest answer is sometimes no. If the insight only exists at the level of detail I cannot publish, then the piece does not get written. That is a real cost and it has to be accepted, because the alternative is publishing something that is either useless or a leak. Writing a hollow version to avoid abandoning the idea is how the generic-content failure mode begins.

Numbers are the easiest thing to get wrong

Scale figures are the detail most likely to be embellished by accident. “Around ten thousand” becomes “over ten thousand” becomes “tens of thousands” across three tellings, and none of those were a decision — they were drift.

I keep the figures I am permitted to use as fixed, approved wording, recorded in the repository, and I copy them rather than restating them. If a number is not on that list, it does not appear. Not a range, not an approximation, not an order of magnitude.

The discipline matters beyond accuracy. A claim that grows in the retelling is the single fastest way to lose credibility with a reader who knows the domain, and the only defence is to have never improvised one.

Writing about code you did not write

This one is not about disclosure. It is about the fact that most estates are collective work.

When I audit a repository and find a destructive bulk operation with no confirmation and no dry run, I have found a genuinely useful lesson. I have also found something a colleague wrote. Publishing “the codebase I inherited was full of unguarded deletions” is technically sanitised and still a bad idea: it is a public characterisation of identifiable people’s work, and the group it describes is small enough for them to recognise themselves.

So I write these strictly in the first person, about patterns I have written or fixed myself. There is always an honest version — everyone who has automated at scale has shipped an unguarded loop at some point, and saying so is both true and more useful than pointing at someone else.

It also happens to be the register that works. Being specific about your own mistakes reads as confidence. Being specific about other people’s reads as something else.

The same logic rules out audit statistics. “This repository had 31 destructive scripts and 16 safety gates” is a vivid sentence and a published scorecard of a team’s engineering quality. The lesson survives without it.

Make it mechanical

Judgement fails when you are tired and pleased with a draft. So the parts that can be mechanical should be.

A tripwire like this one checks staged content for estate-shaped strings and fails if it finds any:

#!/usr/bin/env bash
# Block a publish when staged content still looks like it came from an estate.
set -uo pipefail

patterns='(\.internal|\.corp|\.local)\b'                 # internal suffixes
patterns+='|\b10\.[0-9]{1,3}\.[0-9]{1,3}\.'              # RFC1918
patterns+='|\b192\.168\.'
patterns+='|\b172\.(1[6-9]|2[0-9]|3[01])\.'
patterns+='|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}'         # tenant/account GUIDs

found=0
while IFS= read -r file; do
  [ -f "$file" ] || continue
  if grep -nEi "$patterns" -- "$file"; then
    echo "blocked: $file" >&2
    found=1
  fi
done < <(git diff --cached --name-only -- src/content)

exit "$found"

Three things about this.

It is a tripwire, not a sanitiser. It catches the patterns I thought of. It cannot recognise an internal project name, an unapproved metric or a diagram that leaks its topology. A checklist people trust to catch everything is more dangerous than no checklist, because it replaces attention with a green tick.

It also fires on things that are fine. Run against this article, it blocks it, because the pattern list above contains .internal. Run against the rest of what is published here, it blocks five more pieces: synthetic lab addresses, an app.example.internal hostname and the ID in a public webinar link. Every hit was harmless. A hit means read that line again, not that the piece is unsafe, and a clean run does not mean it is safe.

Review the rendered output, not the source. I read the built page before publishing. Markdown source hides things — image alt text, a frontmatter field, a URL inside a link, a code comment three screens down that was real when it was pasted.

And the build has to pass, because a broken build means the thing I checked is not the thing that ships.

When the answer is no

Sometimes the piece cannot be written. The honest options are to write the general version — the pattern, with a synthetic example built from scratch — or to leave it.

Leaving it is fine. There is always more material than time, and a corpus whose value rests on the reader trusting every claim can afford to lose an article far more easily than it can afford to lose that trust once.

Lessons

Publish the lesson, not the artifact. The artifact belongs to your employer and the lesson belongs to you.

Rewrite; do not redact. Structure leaks, and redaction preserves structure exactly.

Change the shape as well as the strings. Use the number of nodes that makes the example clear, not the number that is real.

Fix your approved figures in writing and copy them. Numbers that drift in the retelling destroy credibility faster than any technical error.

Aim the criticism at your own work. Identifiable colleagues are not material, and the first-person version is the better article anyway.

Automate the string checks and stay suspicious of them. A tripwire buys attention for the judgement calls; it does not replace them.


This article contains no employer specifics by construction. Every example in it is synthetic, and the practice it describes is the one applied to everything else published here.