If you've ever struggled with anxiety, negative self-talk, or emotional spirals, you've probably heard of Cognitive Behavioral Therapy (CBT). It's one of the most evidence-backed therapeutic approaches for anxiety, depression, and dozens of other conditions.
But here's the thing: the core CBT technique — the thought record — is essentially a structured way to debug your own thinking. And if you're a developer, you already know how to debug.
In this tutorial, I'll show you how to build a CBT Thought Record Journal in Python — a CLI tool that guides you through the 7-column thought record, stores entries as JSON, and lets you review patterns over time.
What Is a Thought Record?
A thought record (also called a "dysfunctional thought record" or "ABCDE worksheet") is a structured table where you:
- Situation — Describe what triggered the emotional response
- Emotions — Rate intensity (0-100)
- Automatic Thoughts — What went through your mind?
- Cognitive Distortion — Which thinking trap did you fall into?
- Evidence For — What supports the thought?
- Evidence Against — What contradicts it?
- Balanced Thought — A fairer, more accurate reframe
This structure comes from Aaron Beck's cognitive therapy model. Research shows that regular practice reduces anxiety and depression symptoms by 40-60% over 12-20 sessions.
The 10 Common Cognitive Distortions
Before we code, here are the distortions our tool will detect:
DISTORTIONS = { 'all_or_nothing': 'All-or-Nothing Thinking (splitting)', 'overgeneralization': 'Overgeneralization', 'mental_filter': 'Mental Filter (focusing only on negatives)', 'disqualifying_positive': 'Disqualifying the Positive', 'mind_reading': 'Jumping to Conclusions (Mind Reading)', 'fortune_telling': 'Jumping to Conclusions (Fortune Telling)', 'magnification': 'Magnification (Catastrophizing) or Minimization', 'emotional_reasoning': 'Emotional Reasoning', 'should_statements': 'Should Statements', 'labeling': 'Labeling and Mislabeling', 'personalization': 'Personalization and Blame', } Building the Tool
Step 1: Data Model
from dataclasses import dataclass, field from datetime import datetime from typing import List import json @dataclass class ThoughtRecord: date: str = field(default_factory=lambda: datetime.now().isoformat()) situation: str = '' emotions: dict = field(default_factory=dict) automatic_thoughts: str = '' distortions: List[str] = field(default_factory=list) evidence_for: str = '' evidence_against: str = '' balanced_thought: str = '' new_emotion_rating: dict = field(default_factory=dict) def to_dict(self): return { 'date': self.date, 'situation': self.situation, 'emotions': self.emotions, 'automatic_thoughts': self.automatic_thoughts, 'distortions': self.distortions, 'evidence_for': self.evidence_for, 'evidence_against': self.evidence_against, 'balanced_thought': self.balanced_thought, 'new_emotion_rating': self.new_emotion_rating, } Step 2: Distortion Detector
This is where it gets interesting. We can use simple pattern matching to suggest which distortions might be present:
import re def detect_distortions(thought: str) -> list: thought_lower = thought.lower() detected = [] if re.search(r'\b(always|never|completely|totally|ruined|perfect|failure|success)\b', thought_lower): detected.append('all_or_nothing') if re.search(r'\b(every time|nothing ever)\b', thought_lower): detected.append('overgeneralization') if re.search(r'\b(disaster|end of|over for|can.?t handle|unbearable|worst)\b', thought_lower): detected.append('magnification') if re.search(r'\b(should|must|have to|ought to)\b', thought_lower): detected.append('should_statements') if re.search(r'\b(i feel|it feels|feels like)\b', thought_lower): detected.append('emotional_reasoning') if re.search(r'\b(i am a |i.m a |they are a )\b', thought_lower): detected.append('labeling') if re.search(r'\b(they think|they know|everyone thinks|people will)\b', thought_lower): detected.append('mind_reading') if re.search(r'\b(will go wrong|going to fail|this will)\b', thought_lower): detected.append('fortune_telling') return detected This isn't therapy — it's a prompting tool. The user confirms which distortions actually apply. But the pattern matching helps you learn to recognize them.
Step 3: Interactive CLI
def run_thought_record(): print('=' * 60) print(' CBT Thought Record Journal') print('=' * 60) record = ThoughtRecord() record.situation = input('What happened? (Describe the situation):\n> ') print('\nWhat emotions did you feel? (rate 0-100)') print("Type emotion + rating, e.g. 'anxiety 80'. Empty line to finish.") while True: entry = input('> ') if not entry.strip(): break parts = entry.rsplit(' ', 1) if len(parts) == 2 and parts[1].isdigit(): record.emotions[parts[0]] = int(parts[1]) record.automatic_thoughts = input('\nWhat went through your mind?\n> ') detected = detect_distortions(record.automatic_thoughts) if detected: print('\nPossible cognitive distortions detected:') for d in detected: print(f' - {DISTORTIONS[d]}') confirm = input('\nWhich apply? (comma-separate, or all):\n> ') record.distortions = detected if confirm.strip() == 'all' else \ [d.strip() for d in confirm.split(',') if d.strip() in DISTORTIONS] record.evidence_for = input('\nEvidence that supports the thought:\n> ') record.evidence_against = input('\nEvidence against the thought:\n> ') record.balanced_thought = input('\nA fairer, more balanced thought:\n> ') print('\nRe-rate your emotions now (0-100):') for emotion in record.emotions: new_rating = input(f' {emotion} (was {record.emotions[emotion]}): ') if new_rating.isdigit(): record.new_emotion_rating[emotion] = int(new_rating) return record Step 4: Storage + Pattern Review
from pathlib import Path from collections import Counter class ThoughtRecordJournal: def __init__(self, filepath='thought_records.json'): self.filepath = Path(filepath) self.records = self._load() def _load(self): if self.filepath.exists(): with open(self.filepath, 'r') as f: return json.load(f) return [] def add(self, record: ThoughtRecord): self.records.append(record.to_dict()) self._save() def _save(self): with open(self.filepath, 'w') as f: json.dump(self.records, f, indent=2) def review_patterns(self): all_distortions = [] for r in self.records: all_distortions.extend(r.get('distortions', [])) counts = Counter(all_distortions) print('\n--- Your Most Common Distortions ---') for dist, count in counts.most_common(): print(f' {count}x {DISTORTIONS.get(dist, dist)}') print('\n--- Emotion Intensity Trend ---') for r in self.records[-10:]: for emo, rating in r.get('emotions', {}).items(): new = r.get('new_emotion_rating', {}).get(emo, '?') print(f" {r['date'][:10]} {emo}: {rating} -> {new}") Step 5: Putting It Together
def main(): journal = ThoughtRecordJournal() print('CBT Thought Record Journal') print('1. New thought record') print('2. Review patterns') print('3. Export to CSV') choice = input('\nChoice: ') if choice == '1': record = run_thought_record() journal.add(record) print('\nThought record saved.') elif choice == '2': journal.review_patterns() if __name__ == '__main__': main() Why This Matters
I built a full CBT toolkit with 200+ free tools covering specific phobias, chronic illness, burnout, and more. The thought record is the foundation — once you can identify and reframe a cognitive distortion, you have a portable skill that works across every mental health challenge.
The toolkit is free and open source: CBT Toolkit on GitHub
The Science
CBT has one of the strongest evidence bases in mental health:
- Anxiety disorders: 50-60% symptom reduction (Hofmann et al., 2012 meta-analysis of 269 studies)
- Depression: equivalent to SSRI medication in mild-moderate cases (Cuijpers et al., 2013)
- Insomnia: 70-80% improvement (CBT-I specifically)
- PTSD: recommended first-line treatment by APA, NICE, and WHO
The thought record targets cognitive restructuring — the process of identifying, evaluating, and modifying distorted thinking patterns. It's the CBT equivalent of git bisect for your mind: systematically narrowing down where the bug (distortion) is.
Beyond the CLI
From here, you could extend this tool to:
- Web app (Flask/FastAPI) with a nice UI for the 7-column table
- Mobile app (React Native) with push reminders
- NLP-powered distortion detection using a fine-tuned model
- Pattern visualization with matplotlib showing emotion trends
- Export to PDF for sharing with a therapist
The core data model and distortion detector stay the same — only the interface changes.
Takeaway
Building mental health tools is deeply rewarding. You're not just writing code — you're creating something that could help someone understand their own mind a little better. And the thought record is the perfect starting point: it's structured, evidence-based, and translates naturally into a data model.
If you found this helpful, check out the CBT Toolkit — 200+ free CBT tools for specific conditions, all open source.
Disclaimer: This tool is for self-help and education, not a replacement for professional therapy. If you're struggling, please reach out to a licensed therapist or a crisis line (988 in the US).
