Introduction
Most engineers keep track of what they've built. Far fewer keep track of what they don't understand. We maintain changelogs, architecture decision records, runbooks, and postmortems-all artifacts that document knowledge after the fact. But the gap between "I think I understand this" and "I can actually explain this clearly to someone else" is where a huge amount of engineering risk lives. That gap rarely gets written down anywhere, which means it rarely gets closed on purpose. It just quietly shrinks or grows depending on whether you happen to hit the right problem at the right time.
This article proposes a concrete practice: a "things I do not know" journal, built on the foundation of the Feynman Technique, a learning method associated with physicist Richard Feynman and popularized through his memoir Surely You're Joking, Mr. Feynman! (Feynman & Leighton, 1985). The idea is simple to state and surprisingly hard to do consistently: instead of logging what you've learned, you log what you've discovered you don't understand, and you use a disciplined explanation process to close those gaps over time. For engineers aiming toward architecture roles-where the cost of unexamined assumptions compounds across systems and teams-this habit is not a nice-to-have. It is a risk-management tool disguised as a personal journal.
Context: Why Engineers Need a Different Kind of Journal
Software engineering has an unusual relationship with confidence. Early in a career, the gap between what you know and what you think you know is often wide, but you're aware of it-everything feels uncertain, so you ask questions constantly. As experience accumulates, something strange happens: the felt sense of uncertainty shrinks faster than the actual gap does. You've seen enough systems, read enough code, and shipped enough features that confidence outpaces calibration. This pattern has a name in the psychology literature-the Dunning-Kruger effect, described in Kruger and Dunning's 1999 paper "Unskilled and Unaware of It" (Kruger & Dunning, 1999)-though its popular interpretation is often oversimplified. The more precise and useful takeaway for engineers is not "incompetent people are overconfident", but rather that self-assessment of competence is a skill in its own right, separate from the competence being assessed, and it degrades without deliberate feedback.
Architects are especially exposed to this dynamic because their decisions operate at a level of abstraction where feedback loops are long. A junior engineer who misunderstands a function signature finds out within minutes, when the code fails to compile or the test fails. An architect who misunderstands a consistency guarantee in a distributed system, or the actual failure modes of a managed service they chose two years ago, might not find out until a production incident makes it unavoidable. The cost of an unexamined gap scales with the blast radius of the decisions built on top of it. This is precisely why Neal Ford and Mark Richards, in Fundamentals of Software Architecture (Ford & Richards, O'Reilly, 2020), frame architectural decision-making as inherently about trade-off analysis under uncertainty-an activity that is only as good as the architect's honest accounting of what they actually know versus what they are assuming.
A journal of unknowns addresses this directly by externalizing the gap instead of letting it live silently in your head. It converts a vague, uncomfortable feeling-"I'm not 100% sure how this works"-into a discrete, actionable, trackable item. This is not a new idea in adjacent fields. Scientists keep lab notebooks that record open questions alongside results. Doctors use differential diagnosis lists that explicitly enumerate what they have not ruled out. Engineers, by contrast, tend to only write down what worked, which creates a professional record that is systematically biased toward false confidence.
The Feynman Technique: A Quick Primer
The Feynman Technique, as commonly taught, is a four-step process for testing and deepening understanding. First, you choose a concept and write its name at the top of a blank page. Second, you explain it as if teaching it to someone with no background in the subject, using plain language and no jargon. Third, wherever your explanation stalls, becomes circular, or requires a term you can't define simply, you've found a gap-you go back to source material and fill it. Fourth, you simplify and refine the explanation further, often by finding an analogy, until it reads cleanly from start to finish. The technique is frequently summarized by the phrase "if you can't explain it simply, you don't understand it well enough", a sentiment associated with Feynman though the exact wording is attributed loosely rather than quoted verbatim from a single documented source.
What makes this technique valuable for engineers specifically is that it treats the act of explaining as a diagnostic tool, not just a communication exercise. Writing code that works is not the same as being able to explain why it works, what would break it, and what alternatives were rejected and why. Many engineers have had the experience of confidently answering a question in a meeting, then trying to write that same answer down afterward and discovering it doesn't hold together. The journal formalizes that discovery process instead of leaving it to chance encounters in meetings or interviews.
Designing the "Things I Do Not Know" Journal
A useful unknowns journal needs more structure than a generic notes app, or it devolves into a graveyard of vague anxieties that never get revisited. At minimum, each entry should capture four fields: the specific unknown (phrased as a concrete question, not a vague topic), the context that surfaced it (a ticket, an incident, a code review comment, a conversation), an honest confidence rating for your current understanding, and a status indicating whether it's open, being explained, or resolved. The specificity of the question matters enormously. "I don't really understand Kubernetes" is not an actionable journal entry; "I don't know how kube-proxy decides between iptables and IPVS mode, or what the actual performance difference is at our traffic levels" is something you can research, explain, and close.
The journal should also distinguish between different categories of unknowns, because they require different remediation strategies. Some gaps are factual-you simply haven't read the documentation on a specific API behavior. Others are conceptual-you understand the pieces but haven't built a coherent mental model connecting them, which is common with distributed systems topics like consensus algorithms or eventual consistency. Still others are experiential-you understand the theory of how a system degrades under load, but you've never actually watched it happen, which is a different and often more valuable kind of knowledge. Separating these categories keeps you from treating "read one more blog post" as a universal fix when some gaps genuinely require hands-on practice, like running a chaos experiment or building a toy implementation of a consensus protocol such as Raft.
Finally, the journal benefits from being loosely organized the way a Zettelkasten system is, a note-taking method originating with sociologist Niklas Luhmann and popularized for knowledge workers through tools and writing on atomic, interlinked notes. You don't need Luhmann's full card-index apparatus, but the underlying principle-small, atomic entries that link to related entries-prevents the journal from becoming an undifferentiated list. An entry about not understanding read-after-write consistency guarantees in a particular database should be able to link to an entry about not understanding how your team's caching layer interacts with that same database, because resolving one often illuminates the other.
Implementation: Building a Lightweight Tool
You don't need a complex system to start this practice-a markdown file is enough for the first few weeks. But as the journal grows, a small amount of tooling pays off quickly, particularly around surfacing stale entries and tracking resolution velocity. Below is a minimal TypeScript model for journal entries that could back a CLI tool or a small local web app, designed to be simple enough to extend without a framework.
// unknowns-journal.ts
type UnknownCategory = "factual" | "conceptual" | "experiential";
type UnknownStatus = "open" | "explaining" | "resolved";
interface UnknownEntry {
id: string;
question: string; // must be phrased as a specific, answerable question
category: UnknownCategory;
context: string; // what surfaced this gap
confidenceBefore: number; // 1-5 self-rated confidence
confidenceAfter?: number;
status: UnknownStatus;
createdAt: string;
resolvedAt?: string;
explanationDraft?: string; // the Feynman-style plain explanation
linkedEntryIds: string[];
}
function createEntry(params: Omit<UnknownEntry, "id" | "createdAt" | "status" | "linkedEntryIds">): UnknownEntry {
return {
...params,
id: crypto.randomUUID(),
status: "open",
createdAt: new Date().toISOString(),
linkedEntryIds: [],
};
}
function resolveEntry(entry: UnknownEntry, explanation: string, confidenceAfter: number): UnknownEntry {
if (entry.confidenceBefore >= confidenceAfter) {
console.warn(
`Entry "${entry.question}" resolved without reported confidence gain. Re-check the explanation.`
);
}
return {
...entry,
status: "resolved",
explanationDraft: explanation,
confidenceAfter,
resolvedAt: new Date().toISOString(),
};
}
// Surfacing entries that have gone stale is the mechanism that keeps
// the journal from becoming a write-only log of good intentions.
function findStaleOpenEntries(entries: UnknownEntry[], staleDays = 14): UnknownEntry[] {
const cutoff = Date.now() - staleDays * 24 * 60 * 60 * 1000;
return entries.filter(
(e) => e.status === "open" && new Date(e.createdAt).getTime() < cutoff
);
}
The findStaleOpenEntries function is arguably the most important part of this design, not the schema. Journals fail not because people don't start them, but because entries accumulate without review, and the practice quietly dies. A small script that runs weekly-or a simple reminder built on a cron job or calendar integration-that surfaces anything untouched for two weeks forces a decision: either commit time to explaining it, deprioritize it explicitly, or admit it no longer matters. All three outcomes are more useful than silent neglect.
Practical Examples
Consider a mid-level backend engineer who, while debugging a slow endpoint, discovers they don't actually understand how their ORM's connection pooling interacts with their database's max connection limit under load. Rather than fixing the symptom and moving on, they log the entry: "I don't know how our ORM decides when to open a new connection versus queue a request, or what happens when the pool is exhausted". Over the next few days, during slack time, they read the ORM's pooling documentation, trace through the relevant source code, and write a plain-language explanation as if teaching a new hire. Writing that explanation reveals a second gap-they don't actually know what their database's max_connections setting is set to in production, or why. That becomes a linked entry. Within a week, both gaps are closed, and the engineer has a written artifact that can be shared with the team, potentially preventing the same debugging detour for someone else.
A more architecturally relevant example: an engineer preparing to design a new event-driven service realizes, while writing the proposal, that they've been using the terms "at-least-once" and "exactly-once" delivery somewhat interchangeably in conversation, without a precise mental model of the trade-offs. Rather than glossing over this in the design doc, they log it as an open unknown, then spend an afternoon working through the Feynman process: writing a plain explanation of each delivery guarantee, explaining why exactly-once delivery is rarely truly achievable across network boundaries without idempotency or deduplication logic, and sketching how their proposed system would need to handle duplicate message processing. The resulting explanation becomes part of the design doc itself, strengthening the proposal and giving reviewers a clearer picture of the trade-offs being made.
A third example illustrates the experiential category. A senior engineer moving toward an architect role notices they've read extensively about the Raft consensus algorithm but have never implemented even a toy version. They log the gap, then spend a weekend building a minimal, single-machine simulation of Raft's leader election process in Python, deliberately introducing network partitions to observe the behavior. The resulting intuition-watching split votes and term increments happen rather than just reading about them-produces a qualitatively different kind of confidence than reading the original Raft paper by Ongaro and Ousterhout (2014) alone would have.
# toy_raft_election.py - a minimal illustration for building experiential
# understanding of leader election, not a production-ready implementation.
import random
from dataclasses import dataclass, field
@dataclass
class Node:
node_id: int
term: int = 0
voted_for: int | None = None
state: str = "follower" # follower, candidate, leader
def start_election(node: Node, peers: list[Node]) -> bool:
node.term += 1
node.state = "candidate"
node.voted_for = node.node_id
votes = 1 # vote for self
for peer in peers:
# A peer grants a vote only if it hasn't already voted this term
# and the candidate's term is at least as current as its own.
if peer.term <= node.term and peer.voted_for is None:
peer.voted_for = node.node_id
peer.term = node.term
votes += 1
majority = (len(peers) + 1) // 2 + 1
if votes >= majority:
node.state = "leader"
return True
node.state = "follower"
return False
def simulate_partition_scenario():
nodes = [Node(node_id=i) for i in range(5)]
# Simulate a network partition by only letting node 0 see two peers
reachable_peers = random.sample(nodes[1:], 2)
became_leader = start_election(nodes[0], reachable_peers)
print(f"Node 0 became leader during partition: {became_leader}")
print(f"Votes required for majority: {(len(nodes) + 1) // 2 + 1}")
if __name__ == "__main__":
simulate_partition_scenario()
Trade-offs and Pitfalls
This practice is not free, and pretending otherwise would undermine the credibility of the recommendation. The most obvious cost is time. Writing a clear explanation of something takes meaningfully longer than passively reading about it, and if you journal every unknown that surfaces during a normal week, you will quickly accumulate more open entries than you can realistically close. The discipline required is not just in writing entries but in triaging them-accepting that some gaps are low-stakes enough to leave open indefinitely, and that trying to close every single one is itself a failure mode, a kind of productive-feeling procrastination that substitutes journaling for shipping.
There's also a psychological risk worth naming directly. For engineers prone to impostor syndrome, a journal that is explicitly a running list of things you don't know can become a source of anxiety rather than growth if it's framed incorrectly. The framing matters: this is not a confession log or a performance review input, and it should never be used as one by a manager or a team. It works only when it's treated as a private or team-safe space for intellectual honesty, where logging an unknown is understood as a sign of rigor, not a sign of inadequacy. Teams that try to formalize this practice should be careful never to tie journal contents to performance evaluation, or the practice will immediately be gamed into uselessness, with engineers logging only trivial, easily-resolved unknowns to pad a sense of productivity while hiding anything that would reveal a genuine weak spot.
Best Practices
The practice works best when it's anchored to real work rather than treated as abstract study. Unknowns discovered while debugging, designing, or reviewing code carry more weight than unknowns discovered by browsing a list of technologies you feel like you should know. This is partly a matter of motivation-gaps tied to a real problem are more interesting to close-and partly a matter of relevance, since not every possible unknown is worth your limited time, and the ones surfaced by actual work are, by construction, the ones currently affecting you.
Review cadence matters more than entry volume. A weekly fifteen-minute review, where you scan open entries, close what you can, and explicitly deprioritize what you won't get to, keeps the journal alive. Without this cadence, the journal becomes exactly the kind of write-only artifact it was meant to replace. Some engineers pair this review with spaced repetition tools like Anki, particularly for factual gaps that benefit from reinforcement over time, drawing on the research summarized in Make It Stick by Brown, Roediger, and McDaniel (2014), which documents how spaced retrieval practice outperforms passive review for long-term retention.
It also helps to make the explanation step genuinely public, at least some of the time. Writing an explanation for yourself is useful, but writing it for a specific, less-experienced colleague-or actually delivering it as a five-minute team share, a short internal wiki page, or a comment on the relevant pull request-adds a layer of accountability and catches hand-waving that a private note might let slide. This mirrors a well-known phenomenon in teaching: explaining something to someone who will ask follow-up questions reveals gaps that explaining to an imagined, silent audience does not.
Lastly, track confidence explicitly, before and after, even with a crude 1-to-5 scale. Over months, this creates a personal calibration record-a way of checking whether your sense of "I understand this now" correlates with actually being tested on it later, in production or in conversation. This is the direct antidote to the Dunning-Kruger-style miscalibration discussed earlier in this article, because it turns self-assessment into a measured, revisited quantity instead of a one-time gut feeling.
Key Takeaways
- Log unknowns as specific, answerable questions, not vague topic labels-"I don't understand Kubernetes" is not actionable, but a precise question about a specific mechanism is.
- Categorize gaps as factual, conceptual, or experiential, since each requires a different remediation strategy, from reading documentation to building a toy implementation.
- Build in a stale-entry review mechanism, even a simple script or weekly fifteen-minute ritual, so the journal doesn't become a graveyard of good intentions.
- Rate your confidence before and after resolving each entry to build a long-term calibration record of how well your felt understanding matches reality.
- Never tie this journal to performance evaluation; its value depends entirely on it being a safe space for intellectual honesty.
Analogies and Mental Models
Think of this journal as a defect-tracking system, but for your own mental models instead of your codebase. Just as a bug tracker separates reported issues from fixed ones and tracks severity and status, the unknowns journal separates "known gap, unaddressed" from "gap, currently being investigated" from "gap, closed and verified". The analogy is useful because it reframes the emotional posture toward the journal: nobody treats an open bug ticket as a personal failure, it's simply an accurate record of current system state. The same neutrality should apply to an open entry in your unknowns journal-it's an accurate record of your current understanding, not a verdict on your competence.
A second useful mental model comes from aviation and medicine: the checklist and the differential diagnosis. A pilot's pre-flight checklist doesn't assume the pilot is incompetent; it assumes that under pressure, humans reliably skip steps they're confident about, and that writing things down corrects for a known cognitive bias rather than a personal failing. A doctor's differential diagnosis list explicitly keeps unlikely explanations visible until they're ruled out, resisting the pull toward premature certainty. Your unknowns journal functions the same way for engineering judgment: it resists the pull toward false confidence by keeping the gaps visible on paper instead of letting them fade from awareness the moment you stop actively thinking about them.
The 80/20 Insight
Not every part of this practice contributes equally to the outcome, and it's worth being honest about where the real value concentrates. The single highest-leverage habit is the act of writing a plain-language explanation and noticing exactly where it breaks down-this is the actual Feynman mechanism, and everything else in this article is scaffolding around it. You could skip the fancy tooling, the categorization scheme, and even the confidence ratings, and still get most of the benefit from just doing this one thing consistently: when you notice uncertainty, stop and try to explain the concept simply, in writing, and pay close attention to the exact sentence where the explanation gets vague or circular.
The second highest-leverage element is the review cadence, not the journal's structure. A sophisticated schema with categories and linked entries is worthless if entries are never revisited, while even a single plain-text file with a weekly fifteen-minute review produces real, compounding results. If you're deciding where to invest effort in setting this practice up, invest it in protecting the recurring review time on your calendar, not in building the perfect tool. The tooling in this article is meant to support that habit, not replace it-and if the tooling ever becomes the thing you spend time on instead of the explaining and reviewing, it has stopped serving its purpose.
Conclusion
The things-I-do-not-know journal is a small practice with an outsized effect on long-term engineering judgment, precisely because it targets a blind spot that normal engineering workflows don't address: the silent gap between confident action and verified understanding. Most of software engineering's documentation culture is backward-looking, recording what was built and decided. This practice is forward-looking in a specific sense-it records what still needs to be understood before the next decision is made with real confidence instead of borrowed confidence.
For engineers moving toward architecture roles, where decisions carry longer feedback loops and larger blast radii, this habit compounds in a way that's hard to replicate through any other single practice. It won't replace reading, pairing, code review, or hands-on experience, and it shouldn't be oversold as some singular silver bullet. But as a cheap, low-infrastructure complement to those things-one that costs a notebook, a markdown file, or a small script, and fifteen minutes a week-it's one of the better returns on effort available to anyone serious about actually understanding the systems they're responsible for, rather than merely feeling like they do.
References
- Feynman, R. & Leighton, R. (1985). Surely You're Joking, Mr. Feynman!: Adventures of a Curious Character. W. W. Norton & Company.
- Kruger, J., & Dunning, D. (1999). "Unskilled and Unaware of It: How Difficulties in Recognizing One's Own Incompetence Lead to Inflated Self-Assessments". Journal of Personality and Social Psychology, 77(6), 1121-1134.
- Ford, N., & Richards, M. (2020). Fundamentals of Software Architecture: An Engineering Approach. O'Reilly Media.
- Ongaro, D., & Ousterhout, J. (2014). "In Search of an Understandable Consensus Algorithm (Raft)". Proceedings of the 2014 USENIX Annual Technical Conference.
- Brown, P. C., Roediger, H. L., & McDaniel, M. A. (2014). Make It Stick: The Science of Successful Learning. Belknap Press of Harvard University Press.
- Ahrens, S. (2017). How to Take Smart Notes: One Simple Technique to Boost Writing, Learning and Thinking. CreateSpace Independent Publishing Platform. (Overview of the Zettelkasten method originated by Niklas Luhmann.)
- Dweck, C. S. (2006). Mindset: The New Psychology of Success. Random House.
- Anki - open-source spaced repetition software, https://apps.ankiweb.net/