Skip to main content

4DDR Storage Strategies

Choosing the right storage strategy for your 4DDRs depends on your team size, compliance requirements, analytics needs, and existing toolchain. This guide explores three primary approaches: Git-based, database-driven, and hybrid solutions.

Storage Options Overview

Storage TypeBest ForProsCons
Git-onlySmall teams, Git-first workflowsZero infrastructure, version control, PR workflowLimited querying, no analytics
Database-onlyLarge orgs, complex queryingAnalytics, search, cross-project visibilityExtra infrastructure, separate workflow
HybridTeams wanting both benefitsBest of both worldsComplex setup, sync overhead

Git-Based Storage

Structure

Store 4DDRs as Markdown files with YAML frontmatter in your Git repository:

.gise/
4ddr/
0001-user-research-method.md
0002-frontend-framework.md
0003-database-choice.md
0004-deployment-strategy.md
templates/
4ddr-template.md
discover-4ddr-template.md
design-4ddr-template.md
develop-4ddr-template.md
deploy-4ddr-template.md

Example 4DDR File

---
id: 4DDR-0001
status: accepted
phase: discover
decision: "Use qualitative interviews for user research"
context: "Building MVP with unknown user needs, limited budget for research"
consequences: "Deeper insights but smaller sample size, requires interviewing skills"
alternatives: ["Quantitative surveys", "A/B testing", "Analytics review"]
author: "Product Team"
date: "2025-07-22"
tags: ["user-research", "mvp", "discovery"]
---

# 4DDR-0001: Use Qualitative Interviews for User Research

## Background

We need to understand user needs for our new product MVP. Budget constraints limit our research options, and we need actionable insights quickly.

## Decision

We will conduct 8-12 qualitative user interviews with potential customers rather than running a large quantitative survey.

## Implementation

- Schedule 45-minute interviews with diverse user segments
- Use structured interview guide with open-ended questions
- Record sessions (with permission) for later analysis
- Synthesize findings into user personas and journey maps

## Success Metrics

- Complete interviews within 3 weeks
- Identify 3-5 key user pain points
- Validate or invalidate 2 key product assumptions

Git Workflow Integration

PR Template with 4DDR Requirements

## Description
Brief description of changes

## 4DDR Requirements
- [ ] New decisions documented in 4DDR format
- [ ] Related 4DDRs updated if necessary
- [ ] 4DDR IDs linked in commit messages

## Decision Records
<!-- Link any new or updated 4DDRs -->
- 4DDR-XXXX: [Decision Title](path/to/4ddr.md)

## Checklist
- [ ] Code follows project standards
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] 4DDRs created/updated

Git Hooks for Validation

#!/bin/bash
# .git/hooks/pre-commit
# Validate 4DDR format before commit

for file in $(git diff --cached --name-only | grep '.gise/4ddr/.*\.md$'); do
if ! validate-4ddr-format "$file"; then
echo "❌ Invalid 4DDR format: $file"
echo "Run 'lint-4ddr $file' for details"
exit 1
fi
done

echo "✅ All 4DDRs validated successfully"

Database Storage

Schema Design

For teams needing advanced querying and analytics:

-- Core decision records table
CREATE TABLE decisions (
id VARCHAR(50) PRIMARY KEY,
status VARCHAR(20) NOT NULL,
phase VARCHAR(20) NOT NULL,
decision TEXT NOT NULL,
context TEXT,
consequences TEXT,
alternatives JSONB,
author VARCHAR(100),
created_date TIMESTAMP DEFAULT NOW(),
updated_date TIMESTAMP DEFAULT NOW(),
tags JSONB,
project_id VARCHAR(50),

CONSTRAINT valid_status CHECK (status IN ('proposed', 'accepted', 'rejected', 'superseded', 'deprecated', 'archived')),
CONSTRAINT valid_phase CHECK (phase IN ('discover', 'design', 'develop', 'deploy'))
);

-- Decision relationships
CREATE TABLE decision_relationships (
from_decision VARCHAR(50) REFERENCES decisions(id),
to_decision VARCHAR(50) REFERENCES decisions(id),
relationship_type VARCHAR(50), -- 'supersedes', 'depends-on', 'relates-to'
created_date TIMESTAMP DEFAULT NOW(),

PRIMARY KEY (from_decision, to_decision, relationship_type)
);

-- Decision lifecycle tracking
CREATE TABLE decision_history (
id SERIAL PRIMARY KEY,
decision_id VARCHAR(50) REFERENCES decisions(id),
old_status VARCHAR(20),
new_status VARCHAR(20),
changed_by VARCHAR(100),
change_reason TEXT,
changed_date TIMESTAMP DEFAULT NOW()
);

-- Full-text search index
CREATE INDEX idx_decisions_search ON decisions USING GIN (
to_tsvector('english', decision || ' ' || COALESCE(context, '') || ' ' || COALESCE(consequences, ''))
);

Database Benefits

Advanced Querying

-- Find all design decisions affecting authentication
SELECT * FROM decisions
WHERE phase = 'design'
AND status = 'accepted'
AND (decision ILIKE '%auth%' OR context ILIKE '%auth%')
ORDER BY created_date DESC;

-- Decision dependency analysis
WITH RECURSIVE decision_deps AS (
SELECT from_decision, to_decision, 1 as depth
FROM decision_relationships
WHERE relationship_type = 'depends-on'

UNION ALL

SELECT dr.from_decision, dr.to_decision, dd.depth + 1
FROM decision_relationships dr
JOIN decision_deps dd ON dr.from_decision = dd.to_decision
WHERE dd.depth < 5 -- Prevent infinite loops
)
SELECT d.decision, dd.depth
FROM decision_deps dd
JOIN decisions d ON d.id = dd.to_decision
WHERE dd.from_decision = '4DDR-0023'
ORDER BY dd.depth;

Analytics Dashboard

-- Decision metrics by phase
SELECT
phase,
status,
COUNT(*) as count,
AVG(EXTRACT(DAYS FROM updated_date - created_date)) as avg_decision_time_days
FROM decisions
WHERE created_date >= NOW() - INTERVAL '6 months'
GROUP BY phase, status
ORDER BY phase, status;

-- Most changed decisions (potential pain points)
SELECT
d.id,
d.decision,
COUNT(dh.id) as change_count
FROM decisions d
JOIN decision_history dh ON d.id = dh.decision_id
WHERE dh.changed_date >= NOW() - INTERVAL '3 months'
GROUP BY d.id, d.decision
HAVING COUNT(dh.id) > 3
ORDER BY change_count DESC;

Hybrid Approach

Architecture

Combine Git as the source of truth with database for analytics:

Implementation

GitHub Action for Sync

name: Sync 4DDRs to Database
on:
push:
branches: [main]
paths: ['.gise/4ddr/**']

jobs:
sync-4ddrs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for proper sync

- name: Extract changed 4DDRs
id: changes
run: |
git diff --name-only HEAD~1 HEAD | grep '\.gise/4ddr/.*\.md$' > changed_files.txt
echo "changed_count=$(wc -l < changed_files.txt)" >> $GITHUB_OUTPUT

- name: Parse and sync 4DDRs
if: steps.changes.outputs.changed_count > 0
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
while IFS= read -r file; do
node scripts/sync-4ddr-to-db.js "$file"
done < changed_files.txt

Sync Script Example

// scripts/sync-4ddr-to-db.js
const fs = require('fs');
const yaml = require('yaml');
const { Pool } = require('pg');

const pool = new Pool({
connectionString: process.env.DATABASE_URL
});

async function sync4DDR(filepath) {
const content = fs.readFileSync(filepath, 'utf8');
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);

if (!frontmatterMatch) {
throw new Error(`No frontmatter found in ${filepath}`);
}

const data = yaml.parse(frontmatterMatch[1]);

await pool.query(`
INSERT INTO decisions (id, status, phase, decision, context, consequences, alternatives, author, created_date, tags)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT (id) DO UPDATE SET
status = EXCLUDED.status,
phase = EXCLUDED.phase,
decision = EXCLUDED.decision,
context = EXCLUDED.context,
consequences = EXCLUDED.consequences,
alternatives = EXCLUDED.alternatives,
updated_date = NOW()
`, [
data.id, data.status, data.phase, data.decision,
data.context, data.consequences, JSON.stringify(data.alternatives),
data.author, data.date, JSON.stringify(data.tags)
]);

console.log(`✅ Synced ${data.id}: ${data.decision}`);
}

if (require.main === module) {
sync4DDR(process.argv[2])
.then(() => process.exit(0))
.catch(err => {
console.error('❌ Sync failed:', err);
process.exit(1);
});
}

Storage Strategy Decision Matrix

Choose your approach based on these factors:

FactorGit-OnlyDatabase-OnlyHybrid
Team Size< 10 developers> 50 developers10-50 developers
ProjectsSingle projectMultiple projectsMultiple related projects
Analytics NeedsBasic/NoneAdvancedModerate
ComplianceBasicAdvancedAdvanced
InfrastructureNoneDatabase + UIDatabase + CI/CD
MaintenanceLowMediumHigh
Query ComplexitySimpleComplexComplex
CostFreeMediumMedium-High

Migration Strategies

From Git-Only to Hybrid

  1. Set up database schema
  2. Create initial ETL to backfill existing 4DDRs
  3. Set up sync pipeline
  4. Validate data consistency
  5. Enable analytics/dashboards

From Database-Only to Hybrid

  1. Export existing decisions to Git format
  2. Set up Git repository structure
  3. Implement bi-directional sync
  4. Train team on Git workflow
  5. Phase out direct database updates

Best Practices by Storage Type

Git-Only Best Practices

  • Use consistent file naming: NNNN-kebab-case-title.md
  • Leverage Git tags for versioning
  • Use GitHub/GitLab features for search and organization
  • Implement automated validation via Git hooks
  • Create templates for consistency

Database-Only Best Practices

  • Design schema for your query patterns
  • Implement proper indexing strategy
  • Use database migrations for schema changes
  • Set up regular backups and disaster recovery
  • Implement audit logging for compliance

Hybrid Best Practices

  • Keep Git as single source of truth
  • Implement automated sync with conflict detection
  • Use database for read-heavy operations
  • Monitor sync lag and failures
  • Have rollback procedures for sync issues

Conclusion

Your 4DDR storage strategy should align with your team's workflow, technical capabilities, and organizational needs:

  • Start with Git if you're just beginning with 4DDRs
  • Add database layer when you need analytics or cross-project visibility
  • Consider hybrid if you want developer-friendly workflows with powerful querying

The key is to choose an approach you can maintain consistently over time, as the value of 4DDRs comes from sustained, disciplined practice rather than perfect tooling.